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