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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.server.extensions;
028
029import java.util.Collections;
030import java.util.List;
031import java.util.SortedMap;
032
033import org.forgerock.i18n.LocalizableMessage;
034import org.forgerock.i18n.slf4j.LocalizedLogger;
035import org.forgerock.opendj.config.server.ConfigException;
036import org.opends.server.admin.server.ConfigurationChangeListener;
037import org.opends.server.admin.std.server.EntryCacheCfg;
038import org.opends.server.api.Backend;
039import org.opends.server.api.BackendInitializationListener;
040import org.opends.server.api.EntryCache;
041import org.opends.server.core.DirectoryServer;
042import org.opends.server.types.Attribute;
043import org.forgerock.opendj.config.server.ConfigChangeResult;
044import org.opends.server.types.DN;
045import org.opends.server.types.Entry;
046import org.opends.server.types.InitializationException;
047
048/**
049 * This class defines the default entry cache which acts as an arbiter for
050 * every entry cache implementation configured and installed within the
051 * Directory Server or acts an an empty cache if no implementation specific
052 * entry cache is configured.  It does not actually store any entries, so
053 * all calls to the entry cache public API are routed to underlying entry
054 * cache according to the current configuration order and preferences.
055 */
056public class DefaultEntryCache
057       extends EntryCache<EntryCacheCfg>
058       implements ConfigurationChangeListener<EntryCacheCfg>,
059       BackendInitializationListener
060{
061  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
062
063
064  /**
065   * The entry cache order array reflects all currently configured and
066   * active entry cache implementations in cache level specific order.
067   */
068  private static EntryCache<? extends EntryCacheCfg>[] cacheOrder =
069    new EntryCache<?>[0];
070
071
072  /**
073   * Creates a new instance of this default entry cache.
074   */
075  public DefaultEntryCache()
076  {
077    super();
078
079    // Register with backend initialization listener to clear cache
080    // entries belonging to given backend that about to go offline.
081    DirectoryServer.registerBackendInitializationListener(this);
082  }
083
084  /** {@inheritDoc} */
085  @Override
086  public void initializeEntryCache(EntryCacheCfg configEntry)
087         throws ConfigException, InitializationException
088  {
089    // No implementation required.
090  }
091
092  /** {@inheritDoc} */
093  @Override
094  public void finalizeEntryCache()
095  {
096    for (EntryCache<?> entryCache : cacheOrder) {
097      entryCache.finalizeEntryCache();
098    }
099    // ReInitialize cache order array.
100    cacheOrder = new EntryCache<?>[0];
101  }
102
103  /** {@inheritDoc} */
104  @Override
105  public boolean containsEntry(DN entryDN)
106  {
107    if (entryDN == null) {
108      return false;
109    }
110
111    for (EntryCache<?> entryCache : cacheOrder) {
112      if (entryCache.containsEntry(entryDN)) {
113        return true;
114      }
115    }
116
117    return false;
118  }
119
120  /** {@inheritDoc} */
121  @Override
122  public Entry getEntry(String backendID, long entryID)
123  {
124    for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder)
125    {
126      Entry entry = entryCache.getEntry(backendID, entryID);
127      if (entry != null)
128      {
129        return entry.duplicate(true);
130      }
131    }
132
133    // Indicate global cache miss.
134    if (cacheOrder.length != 0)
135    {
136      cacheMisses.getAndIncrement();
137    }
138    return null;
139  }
140
141  /** {@inheritDoc} */
142  @Override
143  public Entry getEntry(DN entryDN)
144  {
145    for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder)
146    {
147      Entry entry = entryCache.getEntry(entryDN);
148      if (entry != null)
149      {
150        return entry.duplicate(true);
151      }
152    }
153
154    // Indicate global cache miss.
155    if (cacheOrder.length != 0)
156    {
157      cacheMisses.getAndIncrement();
158    }
159    return null;
160  }
161
162  /** {@inheritDoc} */
163  @Override
164  public long getEntryID(DN entryDN)
165  {
166    for (EntryCache<?> entryCache : cacheOrder)
167    {
168      long entryID = entryCache.getEntryID(entryDN);
169      if (entryID != -1)
170      {
171        return entryID;
172      }
173    }
174    return -1;
175  }
176
177  /** {@inheritDoc} */
178  @Override
179  public DN getEntryDN(String backendID, long entryID)
180  {
181    for (EntryCache<?> entryCache : cacheOrder)
182    {
183      DN entryDN = entryCache.getEntryDN(backendID, entryID);
184      if (entryDN != null)
185      {
186        return entryDN;
187      }
188    }
189    return null;
190  }
191
192  /** {@inheritDoc} */
193  @Override
194  public void putEntry(Entry entry, String backendID, long entryID)
195  {
196    for (EntryCache<?> entryCache : cacheOrder) {
197      // The first cache in the order which can take this entry
198      // gets it.
199      if (entryCache.filtersAllowCaching(entry)) {
200        entryCache.putEntry(entry.duplicate(false), backendID, entryID);
201        break;
202      }
203    }
204  }
205
206  /** {@inheritDoc} */
207  @Override
208  public boolean putEntryIfAbsent(Entry entry, String backendID, long entryID)
209  {
210    for (EntryCache<?> entryCache : cacheOrder) {
211      // The first cache in the order which can take this entry
212      // gets it.
213      if (entryCache.filtersAllowCaching(entry)) {
214        return entryCache.putEntryIfAbsent(entry.duplicate(false),
215                backendID, entryID);
216      }
217    }
218
219    return false;
220  }
221
222  /** {@inheritDoc} */
223  @Override
224  public void removeEntry(DN entryDN)
225  {
226    for (EntryCache<?> entryCache : cacheOrder) {
227      if (entryCache.containsEntry(entryDN)) {
228        entryCache.removeEntry(entryDN);
229        break;
230      }
231    }
232  }
233
234  /** {@inheritDoc} */
235  @Override
236  public void clear()
237  {
238    for (EntryCache<?> entryCache : cacheOrder) {
239      entryCache.clear();
240    }
241  }
242
243  /** {@inheritDoc} */
244  @Override
245  public void clearBackend(String backendID)
246  {
247    for (EntryCache<?> entryCache : cacheOrder) {
248      entryCache.clearBackend(backendID);
249    }
250  }
251
252  /** {@inheritDoc} */
253  @Override
254  public void clearSubtree(DN baseDN)
255  {
256    for (EntryCache<?> entryCache : cacheOrder) {
257      entryCache.clearSubtree(baseDN);
258    }
259  }
260
261  /** {@inheritDoc} */
262  @Override
263  public void handleLowMemory()
264  {
265    for (EntryCache<?> entryCache : cacheOrder) {
266      entryCache.handleLowMemory();
267    }
268  }
269
270  /** {@inheritDoc} */
271  @Override
272  public boolean isConfigurationChangeAcceptable(
273      EntryCacheCfg configuration,
274      List<LocalizableMessage> unacceptableReasons
275      )
276  {
277    // No implementation required.
278    return true;
279  }
280
281  /** {@inheritDoc} */
282  @Override
283  public ConfigChangeResult applyConfigurationChange(EntryCacheCfg configuration)
284  {
285    // No implementation required.
286    return new ConfigChangeResult();
287  }
288
289  /** {@inheritDoc} */
290  @Override
291  public List<Attribute> getMonitorData()
292  {
293    // The sum of cache hits of all active entry cache implementations.
294    long entryCacheHits = 0;
295    // Common for all active entry cache implementations.
296    long entryCacheMisses = cacheMisses.longValue();
297    // The sum of cache counts of all active entry cache implementations.
298    long currentEntryCacheCount = 0;
299
300    for (EntryCache<?> entryCache : cacheOrder) {
301      // Get cache hits and counts from every active cache.
302      entryCacheHits += entryCache.getCacheHits();
303      currentEntryCacheCount += entryCache.getCacheCount();
304    }
305
306    try {
307      return EntryCacheCommon.getGenericMonitorData(
308        entryCacheHits,
309        entryCacheMisses,
310        null,
311        null,
312        currentEntryCacheCount,
313        null
314        );
315    } catch (Exception e) {
316      logger.traceException(e);
317      return Collections.emptyList();
318    }
319  }
320
321  /** {@inheritDoc} */
322  @Override
323  public Long getCacheCount()
324  {
325    long cacheCount = 0;
326    for (EntryCache<?> entryCache : cacheOrder) {
327      cacheCount += entryCache.getCacheCount();
328    }
329    return cacheCount;
330  }
331
332  /** {@inheritDoc} */
333  @Override
334  public String toVerboseString()
335  {
336    StringBuilder sb = new StringBuilder();
337    for (EntryCache<?> entryCache : cacheOrder)
338    {
339      String s = entryCache.toVerboseString();
340      if (s != null)
341      {
342        sb.append(s);
343      }
344    }
345    String verboseString = sb.toString();
346    return verboseString.length() > 0 ? verboseString : null;
347  }
348
349
350
351  /**
352   * Retrieves the current cache order array.
353   *
354   * @return  The current cache order array.
355   */
356  public final static EntryCache<? extends EntryCacheCfg>[] getCacheOrder()
357  {
358    return DefaultEntryCache.cacheOrder;
359  }
360
361
362
363  /**
364   * Sets the current cache order array.
365   *
366   * @param  cacheOrderMap  The current cache order array.
367   */
368  public final static void setCacheOrder(
369    SortedMap<Integer,
370    EntryCache<? extends EntryCacheCfg>> cacheOrderMap)
371  {
372    DefaultEntryCache.cacheOrder =
373      cacheOrderMap.values().toArray(new EntryCache<?>[0]);
374  }
375
376
377
378  /**
379   * Performs any processing that may be required whenever a backend
380   * is initialized for use in the Directory Server.  This method will
381   * be invoked after the backend has been initialized but before it
382   * has been put into service.
383   *
384   * @param  backend  The backend that has been initialized and is
385   *                  about to be put into service.
386   */
387  @Override
388  public void performBackendPreInitializationProcessing(Backend<?> backend)
389  {
390    // Do nothing.
391  }
392
393
394
395  /**
396   * Performs any processing that may be required whenever a backend
397   * is finalized.  This method will be invoked after the backend has
398   * been taken out of service but before it has been finalized.
399   *
400   * @param  backend  The backend that has been taken out of service
401   *                  and is about to be finalized.
402   */
403  @Override
404  public void performBackendPostFinalizationProcessing(Backend<?> backend)
405  {
406    // Do not clear any backends if the server is shutting down.
407    if (!DirectoryServer.getInstance().isShuttingDown())
408    {
409      clearBackend(backend.getBackendID());
410    }
411  }
412
413  @Override
414  public void performBackendPostInitializationProcessing(Backend<?> backend) {
415    // Nothing to do.
416  }
417
418  @Override
419  public void performBackendPreFinalizationProcessing(Backend<?> backend) {
420    // Nothing to do.
421  }
422}