001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.extensions;
028    import org.opends.messages.Message;
029    
030    import java.util.ArrayList;
031    import java.util.HashSet;
032    import java.util.LinkedHashSet;
033    import java.util.List;
034    import java.util.SortedSet;
035    
036    import org.opends.server.core.DirectoryServer;
037    import org.opends.server.types.Attribute;
038    import org.opends.server.types.AttributeType;
039    import org.opends.server.types.AttributeValue;
040    import org.opends.server.types.DN;
041    import org.opends.server.types.DirectoryException;
042    import org.opends.server.types.ResultCode;
043    import org.opends.server.types.SearchFilter;
044    import org.opends.messages.MessageDescriptor;
045    
046    import static org.opends.server.loggers.ErrorLogger.logError;
047    import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
048    
049    /**
050     * This class provides some common tools to all entry cache implementations.
051     */
052    public class EntryCacheCommon
053    {
054      /**
055       * Configuration phases. Each value identifies a configuration step:
056       * - PHASE_INIT       when invoking method initializeEntryCache()
057       * - PHASE_ACCEPTABLE when invoking method isConfigurationChangeAcceptable()
058       * - PHASE_APPLY      when invoking method applyConfigurationChange()
059       */
060      public static enum ConfigPhase
061      {
062        /**
063         * Indicates that entry cache is in initialization check phase.
064         */
065        PHASE_INIT,
066    
067        /**
068         * Indicates that entry cache is in configuration check phase.
069         */
070        PHASE_ACCEPTABLE,
071    
072        /**
073         * Indicates that entry cache is applying its configuration.
074         */
075        PHASE_APPLY
076      }
077    
078      /**
079       * Error handler used by local methods to report configuration error.
080       * The error handler simplifies the code of initializeEntryCache(),
081       * isConfigurationChangeAcceptable() and applyConfigurationChanges() methods.
082       */
083      public class ConfigErrorHandler
084      {
085        // Configuration phase.
086        private EntryCacheCommon.ConfigPhase _configPhase;
087    
088        // Unacceptable reasons. Used when _configPhase is PHASE_ACCEPTABLE.
089        private List<Message> _unacceptableReasons;
090    
091        // Error messages. Used when _configPhase is PHASE_APPLY.
092        private ArrayList<Message> _errorMessages;
093    
094        // Result code. Used when _configPhase is PHASE_APPLY.
095        private ResultCode _resultCode;
096    
097        // Acceptable Configuration ? Used when _configPhase is PHASE_ACCEPTABLE
098        // or PHASE_APPLY.
099        private boolean _isAcceptable;
100    
101        // Indicates whether administrative action is required or not. Used when
102        // _configPhase is PHASE_APPLY.
103        private boolean _isAdminActionRequired;
104    
105        /**
106         * Create an error handler.
107         *
108         * @param configPhase          the configuration phase for which the
109         *                             error handler is used
110         * @param unacceptableReasons  the reasons why the configuration cannot
111         *                             be applied (during PHASE_ACCEPTABLE phase)
112         * @param errorMessages        the errors found when applying a new
113         *                             configuration (during PHASE_APPLY phase)
114         */
115        public ConfigErrorHandler (
116            EntryCacheCommon.ConfigPhase configPhase,
117            List<Message> unacceptableReasons,
118            ArrayList<Message>            errorMessages
119            )
120        {
121          _configPhase           = configPhase;
122          _unacceptableReasons   = unacceptableReasons;
123          _errorMessages         = errorMessages;
124          _resultCode            = ResultCode.SUCCESS;
125          _isAcceptable          = true;
126          _isAdminActionRequired = false;
127        }
128    
129        /**
130         * Report an error.
131         *
132         * @param error        the error to report
133         * @param isAcceptable <code>true</code> if the configuration is acceptable
134         * @param resultCode   the change result for the current configuration
135         */
136        public void reportError(
137                Message error,
138                boolean isAcceptable,
139                ResultCode resultCode
140        )
141        {
142          switch (_configPhase)
143          {
144          case PHASE_INIT:
145            {
146            _errorMessages.add (error);
147            _isAcceptable = isAcceptable;
148            break;
149            }
150          case PHASE_ACCEPTABLE:
151            {
152            _unacceptableReasons.add (error);
153            _isAcceptable = isAcceptable;
154            break;
155            }
156          case PHASE_APPLY:
157            {
158            _errorMessages.add (error);
159            _isAcceptable = isAcceptable;
160            if (_resultCode == ResultCode.SUCCESS)
161            {
162              _resultCode = resultCode;
163            }
164            break;
165            }
166          }
167        }
168    
169        /**
170         * Report an error.
171         *
172         * @param error        the error to report
173         * @param isAcceptable <code>true</code> if the configuration is acceptable
174         * @param resultCode   the change result for the current configuration
175         * @param isAdminActionRequired <code>true</code> if administrative action
176         *                              is required or <code>false</code> otherwise
177         */
178        public void reportError(
179                Message error,
180                boolean isAcceptable,
181                ResultCode resultCode,
182                boolean isAdminActionRequired
183        )
184        {
185          switch (_configPhase)
186          {
187          case PHASE_INIT:
188            {
189            logError (error);
190            break;
191            }
192          case PHASE_ACCEPTABLE:
193            {
194            _unacceptableReasons.add (error);
195            _isAcceptable = isAcceptable;
196            break;
197            }
198          case PHASE_APPLY:
199            {
200            _errorMessages.add (error);
201            _isAcceptable = isAcceptable;
202            if (_resultCode == ResultCode.SUCCESS)
203            {
204              _resultCode = resultCode;
205            }
206            _isAdminActionRequired = isAdminActionRequired;
207            break;
208            }
209          }
210        }
211    
212        /**
213         * Get the current result code that was elaborated right after a
214         * configuration has been applied.
215         *
216         * @return the current result code
217         */
218        public ResultCode getResultCode()
219        {
220          return _resultCode;
221        }
222    
223        /**
224         * Get the current isAcceptable flag. The isAcceptable flag is elaborated
225         * right after the configuration was checked.
226         *
227         * @return the isAcceptable flag
228         */
229        public boolean getIsAcceptable()
230        {
231          return _isAcceptable;
232        }
233    
234        /**
235         * Get the current unacceptable reasons. The unacceptable reasons are
236         * elaborated when the configuration is checked.
237         *
238         * @return the list of unacceptable reasons
239         */
240        public List<Message> getUnacceptableReasons()
241        {
242          return _unacceptableReasons;
243        }
244    
245        /**
246         * Get the current error messages. The error messages are elaborated
247         * when the configuration is applied.
248         *
249         * @return the list of error messages
250         */
251        public ArrayList<Message> getErrorMessages()
252        {
253          return _errorMessages;
254        }
255    
256        /**
257         * Get the current configuration phase. The configuration phase indicates
258         * whether the entry cache is in initialization step, or in configuration
259         * checking step or in configuration being applied step.
260         *
261         * @return the current configuration phase.
262         */
263        public ConfigPhase getConfigPhase()
264        {
265          return _configPhase;
266        }
267    
268        /**
269         * Get the current isAdminActionRequired flag as determined after apply
270         * action has been taken on a given configuration.
271         *
272         * @return the isAdminActionRequired flag
273         */
274        public boolean getIsAdminActionRequired()
275        {
276          return _isAdminActionRequired;
277        }
278      } // ConfigErrorHandler
279    
280    
281      /**
282       * Reads a list of string filters and convert it to a list of search
283       * filters.
284       *
285       * @param filters  the list of string filter to convert to search filters
286       * @param decodeErrorMsg  the error message ID to use in case of error
287       * @param errorHandler  error handler to report filter decoding errors on
288       * @param configEntryDN  the entry cache configuration DN
289       *
290       * @return the set of search filters
291       */
292      public static HashSet<SearchFilter> getFilters (
293          SortedSet<String>       filters,
294          MessageDescriptor.Arg3<CharSequence, CharSequence, CharSequence>
295                                  decodeErrorMsg,
296          ConfigErrorHandler      errorHandler,
297          DN                      configEntryDN
298          )
299      {
300        // Returned value
301        HashSet<SearchFilter> searchFilters = new HashSet<SearchFilter>();
302    
303        // Convert the string filters to search filters.
304        if ((filters != null) && (! filters.isEmpty()))
305        {
306          for (String curFilter: filters)
307          {
308            try
309            {
310              searchFilters.add (SearchFilter.createFilterFromString (curFilter));
311            }
312            catch (DirectoryException de)
313            {
314              // We couldn't decode this filter. Report an error and continue.
315              Message message = decodeErrorMsg.get(String.valueOf(configEntryDN),
316                curFilter, (de.getMessage() != null ? de.getMessage() :
317                  stackTraceToSingleLineString(de)));
318              errorHandler.reportError(message, false,
319                ResultCode.INVALID_ATTRIBUTE_SYNTAX);
320            }
321          }
322        }
323    
324        // done
325        return searchFilters;
326      }
327    
328    
329      /**
330       * Create a new error handler.
331       *
332       * @param configPhase          the configuration phase for which the
333       *                             error handler is used
334       * @param unacceptableReasons  the reasons why the configuration cannot
335       *                             be applied (during PHASE_ACCEPTABLE phase)
336       * @param errorMessages        the errors found when applying a new
337       *                             configuration (during PHASE_APPLY phase)
338       *
339       * @return a new configuration error handler
340       */
341      public static ConfigErrorHandler getConfigErrorHandler (
342          EntryCacheCommon.ConfigPhase  configPhase,
343          List<Message> unacceptableReasons,
344          ArrayList<Message>             errorMessages
345          )
346      {
347        ConfigErrorHandler errorHandler = null;
348    
349        EntryCacheCommon ec = new EntryCacheCommon();
350    
351        errorHandler = ec.new ConfigErrorHandler (
352            configPhase, unacceptableReasons, errorMessages
353            );
354        return errorHandler;
355      }
356    
357    
358      /**
359       * Constructs a set of generic attributes containing entry cache
360       * monitor data. Note that <code>null</code> can be passed in
361       * place of any argument to denote the argument is omitted, such
362       * is when no state data of a given kind is available or can be
363       * provided.
364       *
365       * @param cacheHits      number of cache hits.
366       * @param cacheMisses    number of cache misses.
367       * @param cacheSize      size of the current cache, in bytes.
368       * @param maxCacheSize   maximum allowed cache size, in bytes.
369       * @param cacheCount     number of entries stored in the cache.
370       * @param maxCacheCount  maximum number of cache entries allowed.
371       *
372       * @return  A set of generic attributes containing monitor data.
373       */
374      public static ArrayList<Attribute> getGenericMonitorData(
375        Long cacheHits,
376        Long cacheMisses,
377        Long cacheSize,
378        Long maxCacheSize,
379        Long cacheCount,
380        Long maxCacheCount)
381      {
382        ArrayList<Attribute> attrs = new ArrayList<Attribute>();
383    
384        if (cacheHits != null) {
385          AttributeType hitsAttrType =
386            DirectoryServer.getDefaultAttributeType("entryCacheHits");
387          LinkedHashSet<AttributeValue> hitsValues =
388            new LinkedHashSet<AttributeValue>();
389          hitsValues.add(new AttributeValue(hitsAttrType,
390            cacheHits.toString()));
391          attrs.add(new Attribute(hitsAttrType, "entryCacheHits",
392            hitsValues));
393          // Cache misses is required to get cache tries and hit ratio.
394          if (cacheMisses != null) {
395            AttributeType triesAttrType =
396              DirectoryServer.getDefaultAttributeType("entryCacheTries");
397            LinkedHashSet<AttributeValue> triesValues =
398              new LinkedHashSet<AttributeValue>();
399            Long cacheTries = cacheHits + cacheMisses;
400            triesValues.add(new AttributeValue(triesAttrType,
401              cacheTries.toString()));
402            attrs.add(new Attribute(triesAttrType, "entryCacheTries",
403              triesValues));
404    
405            AttributeType hitRatioAttrType =
406              DirectoryServer.getDefaultAttributeType("entryCacheHitRatio");
407            LinkedHashSet<AttributeValue> hitRatioValues =
408              new LinkedHashSet<AttributeValue>();
409            Double hitRatioRaw = cacheTries > 0 ?
410              cacheHits.doubleValue() / cacheTries.doubleValue() :
411              cacheHits.doubleValue() / 1;
412            Double hitRatio = hitRatioRaw * 100D;
413            hitRatioValues.add(new AttributeValue(hitRatioAttrType,
414              Long.toString(hitRatio.longValue())));
415            attrs.add(new Attribute(hitRatioAttrType, "entryCacheHitRatio",
416              hitRatioValues));
417          }
418        }
419    
420        if (cacheSize != null) {
421          AttributeType memoryAttrType =
422            DirectoryServer.getDefaultAttributeType("currentEntryCacheSize");
423          LinkedHashSet<AttributeValue> memoryValues =
424            new LinkedHashSet<AttributeValue>();
425          memoryValues.add(new AttributeValue(memoryAttrType,
426            cacheSize.toString()));
427          attrs.add(new Attribute(memoryAttrType, "currentEntryCacheSize",
428            memoryValues));
429        }
430    
431        if (maxCacheSize != null) {
432          AttributeType maxMemoryAttrType =
433            DirectoryServer.getDefaultAttributeType("maxEntryCacheSize");
434          LinkedHashSet<AttributeValue> maxMemoryValues =
435            new LinkedHashSet<AttributeValue>();
436          maxMemoryValues.add(new AttributeValue(maxMemoryAttrType,
437            maxCacheSize.toString()));
438          attrs.add(new Attribute(maxMemoryAttrType, "maxEntryCacheSize",
439            maxMemoryValues));
440        }
441    
442        if (cacheCount != null) {
443          AttributeType entriesAttrType =
444            DirectoryServer.getDefaultAttributeType("currentEntryCacheCount");
445          LinkedHashSet<AttributeValue> entriesValues =
446            new LinkedHashSet<AttributeValue>();
447          entriesValues.add(new AttributeValue(entriesAttrType,
448            cacheCount.toString()));
449          attrs.add(new Attribute(entriesAttrType, "currentEntryCacheCount",
450            entriesValues));
451        }
452    
453        if (maxCacheCount != null) {
454          AttributeType maxEntriesAttrType =
455            DirectoryServer.getDefaultAttributeType("maxEntryCacheCount");
456          LinkedHashSet<AttributeValue> maxEntriesValues =
457            new LinkedHashSet<AttributeValue>();
458          maxEntriesValues.add(new AttributeValue(maxEntriesAttrType,
459            maxCacheCount.toString()));
460          attrs.add(new Attribute(maxEntriesAttrType, "maxEntryCacheCount",
461            maxEntriesValues));
462        }
463    
464        return attrs;
465      }
466    
467    }
468