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 2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.extensions;
028    import java.lang.reflect.Method;
029    import org.opends.messages.Message;
030    
031    
032    
033    import java.util.ArrayList;
034    import java.util.List;
035    import java.util.SortedMap;
036    import java.util.concurrent.locks.Lock;
037    
038    import org.opends.server.admin.server.ConfigurationChangeListener;
039    import org.opends.server.admin.std.server.EntryCacheCfg;
040    import org.opends.server.api.Backend;
041    import org.opends.server.api.BackendInitializationListener;
042    import org.opends.server.api.EntryCache;
043    import org.opends.server.config.ConfigException;
044    import org.opends.server.core.DirectoryServer;
045    import org.opends.server.loggers.debug.DebugTracer;
046    import org.opends.server.types.Attribute;
047    import org.opends.server.types.ConfigChangeResult;
048    import org.opends.server.types.DN;
049    import org.opends.server.types.DebugLogLevel;
050    import org.opends.server.types.Entry;
051    import org.opends.server.types.InitializationException;
052    import org.opends.server.types.LockType;
053    import org.opends.server.types.ResultCode;
054    
055    import static org.opends.server.loggers.debug.DebugLogger.*;
056    
057    /**
058     * This class defines the default entry cache which acts as an arbiter for
059     * every entry cache implementation configured and installed within the
060     * Directory Server or acts an an empty cache if no implementation specific
061     * entry cache is configured.  It does not actually store any entries, so
062     * all calls to the entry cache public API are routed to underlying entry
063     * cache according to the current configuration order and preferences.
064     */
065    public class DefaultEntryCache
066           extends EntryCache<EntryCacheCfg>
067           implements ConfigurationChangeListener<EntryCacheCfg>,
068           BackendInitializationListener
069    {
070      /**
071       * The tracer object for the debug logger.
072       */
073      private static final DebugTracer TRACER = getTracer();
074    
075    
076      // The entry cache order array reflects all currently configured and
077      // active entry cache implementations in cache level specific order.
078      private static EntryCache<? extends EntryCacheCfg>[] cacheOrder =
079        new EntryCache<?>[0];
080    
081    
082      /**
083       * Creates a new instance of this default entry cache.
084       */
085      public DefaultEntryCache()
086      {
087        super();
088    
089        // Register with backend initialization listener to clear cache
090        // entries belonging to given backend that about to go offline.
091        DirectoryServer.registerBackendInitializationListener(this);
092      }
093    
094    
095      /**
096       * {@inheritDoc}
097       */
098      public void initializeEntryCache(EntryCacheCfg configEntry)
099             throws ConfigException, InitializationException
100      {
101        // No implementation required.
102      }
103    
104    
105      /**
106       * {@inheritDoc}
107       */
108      public void finalizeEntryCache()
109      {
110        for (EntryCache entryCache : cacheOrder) {
111          entryCache.finalizeEntryCache();
112        }
113        // ReInitialize cache order array.
114        cacheOrder = new EntryCache<?>[0];
115      }
116    
117    
118      /**
119       * {@inheritDoc}
120       */
121      public boolean containsEntry(DN entryDN)
122      {
123        if (entryDN == null) {
124          return false;
125        }
126    
127        for (EntryCache entryCache : cacheOrder) {
128          if (entryCache.containsEntry(entryDN)) {
129            return true;
130          }
131        }
132    
133        return false;
134      }
135    
136    
137      /**
138       * {@inheritDoc}
139       */
140      @Override
141      public Entry getEntry(DN entryDN,
142                            LockType lockType,
143                            List<Lock> lockList)
144      {
145        Entry entry = null;
146    
147        for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder) {
148          entry = entryCache.getEntry(entryDN, lockType, lockList);
149          if (entry != null) {
150            break;
151          }
152        }
153    
154        // Indicate global cache miss.
155        if ((entry == null) && (cacheOrder.length != 0)) {
156          cacheMisses.getAndIncrement();
157        }
158    
159        return entry;
160      }
161    
162    
163      /**
164       * {@inheritDoc}
165       */
166      @Override
167      public Entry getEntry(Backend backend, long entryID,
168                                     LockType lockType,
169                                     List<Lock> lockList)
170      {
171        Entry entry = null;
172    
173        for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder) {
174          entry = entryCache.getEntry(backend, entryID, lockType,
175              lockList);
176          if (entry != null) {
177            break;
178          }
179        }
180    
181        // Indicate global cache miss.
182        if ((entry == null) && (cacheOrder.length != 0)) {
183          cacheMisses.getAndIncrement();
184        }
185    
186        return entry;
187      }
188    
189    
190      /**
191       * {@inheritDoc}
192       */
193      public Entry getEntry(DN entryDN)
194      {
195        Entry entry = null;
196    
197        for (EntryCache entryCache : cacheOrder) {
198          entry = entryCache.getEntry(entryDN);
199          if (entry != null) {
200            break;
201          }
202        }
203    
204        // Indicate global cache miss.
205        if ((entry == null) && (cacheOrder.length != 0)) {
206          cacheMisses.getAndIncrement();
207        }
208    
209        return entry;
210      }
211    
212    
213    
214      /**
215       * {@inheritDoc}
216       */
217      public long getEntryID(DN entryDN)
218      {
219        long entryID = -1;
220    
221        for (EntryCache entryCache : cacheOrder) {
222          entryID = entryCache.getEntryID(entryDN);
223          if (entryID != -1) {
224            break;
225          }
226        }
227    
228        return entryID;
229      }
230    
231    
232    
233      /**
234       * {@inheritDoc}
235       */
236      public DN getEntryDN(Backend backend, long entryID)
237      {
238        DN entryDN = null;
239    
240        for (EntryCache entryCache : cacheOrder) {
241          entryDN = entryCache.getEntryDN(backend, entryID);
242          if (entryDN != null) {
243            break;
244          }
245        }
246    
247        return entryDN;
248      }
249    
250    
251    
252      /**
253       * {@inheritDoc}
254       */
255      public void putEntry(Entry entry, Backend backend, long entryID)
256      {
257        for (EntryCache entryCache : cacheOrder) {
258          // The first cache in the order which can take this entry
259          // gets it.
260          if (entryCache.filtersAllowCaching(entry)) {
261            entryCache.putEntry(entry, backend, entryID);
262            break;
263          }
264        }
265      }
266    
267    
268    
269      /**
270       * {@inheritDoc}
271       */
272      public boolean putEntryIfAbsent(Entry entry, Backend backend, long entryID)
273      {
274        for (EntryCache entryCache : cacheOrder) {
275          // The first cache in the order which can take this entry
276          // gets it.
277          if (entryCache.filtersAllowCaching(entry)) {
278            return entryCache.putEntryIfAbsent(entry, backend, entryID);
279          }
280        }
281    
282        return false;
283      }
284    
285    
286    
287      /**
288       * {@inheritDoc}
289       */
290      public void removeEntry(DN entryDN)
291      {
292        for (EntryCache entryCache : cacheOrder) {
293          if (entryCache.containsEntry(entryDN)) {
294            entryCache.removeEntry(entryDN);
295            break;
296          }
297        }
298      }
299    
300    
301    
302      /**
303       * {@inheritDoc}
304       */
305      public void clear()
306      {
307        for (EntryCache entryCache : cacheOrder) {
308          entryCache.clear();
309        }
310      }
311    
312    
313    
314      /**
315       * {@inheritDoc}
316       */
317      public void clearBackend(Backend backend)
318      {
319        for (EntryCache entryCache : cacheOrder) {
320          entryCache.clearBackend(backend);
321        }
322      }
323    
324    
325    
326      /**
327       * {@inheritDoc}
328       */
329      public void clearSubtree(DN baseDN)
330      {
331        for (EntryCache entryCache : cacheOrder) {
332          entryCache.clearSubtree(baseDN);
333        }
334      }
335    
336    
337    
338      /**
339       * {@inheritDoc}
340       */
341      public void handleLowMemory()
342      {
343        for (EntryCache entryCache : cacheOrder) {
344          entryCache.handleLowMemory();
345        }
346      }
347    
348    
349    
350      /**
351       * {@inheritDoc}
352       */
353      public boolean isConfigurationChangeAcceptable(
354          EntryCacheCfg configuration,
355          List<Message> unacceptableReasons
356          )
357      {
358        // No implementation required.
359        return true;
360      }
361    
362    
363    
364      /**
365       * {@inheritDoc}
366       */
367      public ConfigChangeResult applyConfigurationChange(
368          EntryCacheCfg configuration
369          )
370      {
371        // No implementation required.
372        ConfigChangeResult changeResult = new ConfigChangeResult(
373            ResultCode.SUCCESS, false, new ArrayList<Message>()
374            );
375    
376        return changeResult;
377      }
378    
379    
380    
381      /**
382       * {@inheritDoc}
383       */
384      public ArrayList<Attribute> getMonitorData()
385      {
386        ArrayList<Attribute> attrs = new ArrayList<Attribute>();
387    
388        // The sum of cache hits of all active entry cache
389        // implementations.
390        Long entryCacheHits = new Long(0);
391        // Common for all active entry cache implementations.
392        Long entryCacheMisses = new Long(cacheMisses.longValue());
393        // The sum of cache counts of all active entry cache
394        // implementations.
395        Long currentEntryCacheCount = new Long(0);
396    
397        for (EntryCache entryCache : cacheOrder) {
398          // Get cache hits and counts from every active cache.
399          entryCacheHits += entryCache.getCacheHits();
400          currentEntryCacheCount += entryCache.getCacheCount();
401        }
402    
403        try {
404          attrs = EntryCacheCommon.getGenericMonitorData(
405            entryCacheHits,
406            entryCacheMisses,
407            null,
408            null,
409            currentEntryCacheCount,
410            null
411            );
412        } catch (Exception e) {
413          if (debugEnabled()) {
414            TRACER.debugCaught(DebugLogLevel.ERROR, e);
415          }
416        }
417    
418        return attrs;
419      }
420    
421    
422    
423      /**
424       * {@inheritDoc}
425       */
426      public Long getCacheCount()
427      {
428        Long cacheCount = new Long(0);
429    
430        for (EntryCache entryCache : cacheOrder) {
431          cacheCount += entryCache.getCacheCount();
432        }
433    
434        return cacheCount;
435      }
436    
437    
438    
439      /**
440       * Return a verbose string representation of the current cache maps.
441       * This is useful primary for debugging and diagnostic purposes such
442       * as in the entry cache unit tests.
443       * @return String verbose string representation of the current cache
444       *                maps in the following format: dn:id:backend
445       *                one cache entry map representation per line
446       *                or <CODE>null</CODE> if all maps are empty.
447       */
448      private String toVerboseString()
449      {
450        String verboseString = new String();
451        StringBuilder sb = new StringBuilder();
452    
453        for (EntryCache entryCache : cacheOrder) {
454          final Method[] cacheMethods =
455            entryCache.getClass().getDeclaredMethods();
456          for (int i = 0; i < cacheMethods.length; ++i) {
457            if (cacheMethods[i].getName().equals("toVerboseString")) {
458              cacheMethods[i].setAccessible(true);
459              try {
460                Object cacheVerboseString =
461                  cacheMethods[i].invoke(entryCache, (Object[]) null);
462                if (cacheVerboseString != null) {
463                  sb.append((String) cacheVerboseString);
464                }
465              } catch (Exception e) {
466                if (debugEnabled()) {
467                  TRACER.debugCaught(DebugLogLevel.ERROR, e);
468                }
469              }
470            }
471          }
472        }
473    
474        verboseString = sb.toString();
475    
476        return (verboseString.length() > 0 ? verboseString : null);
477      }
478    
479    
480    
481      /**
482       * Retrieves the current cache order array.
483       *
484       * @return  The current cache order array.
485       */
486      public final EntryCache<? extends EntryCacheCfg>[] getCacheOrder()
487      {
488        return this.cacheOrder;
489      }
490    
491    
492    
493      /**
494       * Sets the current cache order array.
495       *
496       * @param  cacheOrderMap  The current cache order array.
497       */
498      public final void setCacheOrder(
499        SortedMap<Integer,
500        EntryCache<? extends EntryCacheCfg>> cacheOrderMap)
501      {
502        this.cacheOrder =
503          cacheOrderMap.values().toArray(new EntryCache<?>[0]);
504      }
505    
506    
507    
508      /**
509       * Performs any processing that may be required whenever a backend
510       * is initialized for use in the Directory Server.  This method will
511       * be invoked after the backend has been initialized but before it
512       * has been put into service.
513       *
514       * @param  backend  The backend that has been initialized and is
515       *                  about to be put into service.
516       */
517      public void performBackendInitializationProcessing(Backend backend)
518      {
519        // Do nothing.
520      }
521    
522    
523    
524      /**
525       * Performs any processing that may be required whenever a backend
526       * is finalized.  This method will be invoked after the backend has
527       * been taken out of service but before it has been finalized.
528       *
529       * @param  backend  The backend that has been taken out of service
530       *                  and is about to be finalized.
531       */
532      public void performBackendFinalizationProcessing(Backend backend)
533      {
534        // Do not clear any backends if the server is shutting down.
535        if ( !(DirectoryServer.getInstance().isShuttingDown()) ) {
536          clearBackend(backend);
537        }
538      }
539    }
540