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.forgerock.opendj.config.server.ConfigurationChangeListener;
027import org.forgerock.opendj.server.config.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   * The entry cache order array reflects all currently configured and
054   * active entry cache implementations in cache level specific order.
055   */
056  private static EntryCache<? extends EntryCacheCfg>[] cacheOrder =
057    new EntryCache<?>[0];
058
059  /** Creates a new instance of this default entry cache. */
060  public DefaultEntryCache()
061  {
062    super();
063
064    // Register with backend initialization listener to clear cache
065    // entries belonging to given backend that about to go offline.
066    DirectoryServer.registerBackendInitializationListener(this);
067  }
068
069  @Override
070  public void initializeEntryCache(EntryCacheCfg configEntry)
071         throws ConfigException, InitializationException
072  {
073    // No implementation required.
074  }
075
076  @Override
077  public void finalizeEntryCache()
078  {
079    for (EntryCache<?> entryCache : cacheOrder) {
080      entryCache.finalizeEntryCache();
081    }
082    // ReInitialize cache order array.
083    cacheOrder = new EntryCache<?>[0];
084  }
085
086  @Override
087  public boolean containsEntry(DN entryDN)
088  {
089    if (entryDN == null) {
090      return false;
091    }
092
093    for (EntryCache<?> entryCache : cacheOrder) {
094      if (entryCache.containsEntry(entryDN)) {
095        return true;
096      }
097    }
098
099    return false;
100  }
101
102  @Override
103  public Entry getEntry(String backendID, long entryID)
104  {
105    for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder)
106    {
107      Entry entry = entryCache.getEntry(backendID, entryID);
108      if (entry != null)
109      {
110        return entry.duplicate(true);
111      }
112    }
113
114    // Indicate global cache miss.
115    if (cacheOrder.length != 0)
116    {
117      cacheMisses.getAndIncrement();
118    }
119    return null;
120  }
121
122  @Override
123  public Entry getEntry(DN entryDN)
124  {
125    for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder)
126    {
127      Entry entry = entryCache.getEntry(entryDN);
128      if (entry != null)
129      {
130        return entry.duplicate(true);
131      }
132    }
133
134    // Indicate global cache miss.
135    if (cacheOrder.length != 0)
136    {
137      cacheMisses.getAndIncrement();
138    }
139    return null;
140  }
141
142  @Override
143  public long getEntryID(DN entryDN)
144  {
145    for (EntryCache<?> entryCache : cacheOrder)
146    {
147      long entryID = entryCache.getEntryID(entryDN);
148      if (entryID != -1)
149      {
150        return entryID;
151      }
152    }
153    return -1;
154  }
155
156  @Override
157  public DN getEntryDN(String backendID, long entryID)
158  {
159    for (EntryCache<?> entryCache : cacheOrder)
160    {
161      DN entryDN = entryCache.getEntryDN(backendID, entryID);
162      if (entryDN != null)
163      {
164        return entryDN;
165      }
166    }
167    return null;
168  }
169
170  @Override
171  public void putEntry(Entry entry, String backendID, long entryID)
172  {
173    for (EntryCache<?> entryCache : cacheOrder) {
174      // The first cache in the order which can take this entry
175      // gets it.
176      if (entryCache.filtersAllowCaching(entry)) {
177        entryCache.putEntry(entry.duplicate(false), backendID, entryID);
178        break;
179      }
180    }
181  }
182
183  @Override
184  public boolean putEntryIfAbsent(Entry entry, String backendID, long entryID)
185  {
186    for (EntryCache<?> entryCache : cacheOrder) {
187      // The first cache in the order which can take this entry
188      // gets it.
189      if (entryCache.filtersAllowCaching(entry)) {
190        return entryCache.putEntryIfAbsent(entry.duplicate(false),
191                backendID, entryID);
192      }
193    }
194
195    return false;
196  }
197
198  @Override
199  public void removeEntry(DN entryDN)
200  {
201    for (EntryCache<?> entryCache : cacheOrder) {
202      if (entryCache.containsEntry(entryDN)) {
203        entryCache.removeEntry(entryDN);
204        break;
205      }
206    }
207  }
208
209  @Override
210  public void clear()
211  {
212    for (EntryCache<?> entryCache : cacheOrder) {
213      entryCache.clear();
214    }
215  }
216
217  @Override
218  public void clearBackend(String backendID)
219  {
220    for (EntryCache<?> entryCache : cacheOrder) {
221      entryCache.clearBackend(backendID);
222    }
223  }
224
225  @Override
226  public void clearSubtree(DN baseDN)
227  {
228    for (EntryCache<?> entryCache : cacheOrder) {
229      entryCache.clearSubtree(baseDN);
230    }
231  }
232
233  @Override
234  public void handleLowMemory()
235  {
236    for (EntryCache<?> entryCache : cacheOrder) {
237      entryCache.handleLowMemory();
238    }
239  }
240
241  @Override
242  public boolean isConfigurationChangeAcceptable(
243      EntryCacheCfg configuration,
244      List<LocalizableMessage> unacceptableReasons
245      )
246  {
247    // No implementation required.
248    return true;
249  }
250
251  @Override
252  public ConfigChangeResult applyConfigurationChange(EntryCacheCfg configuration)
253  {
254    // No implementation required.
255    return new ConfigChangeResult();
256  }
257
258  @Override
259  public MonitorData getMonitorData()
260  {
261    // The sum of cache hits of all active entry cache implementations.
262    long entryCacheHits = 0;
263    // Common for all active entry cache implementations.
264    long entryCacheMisses = cacheMisses.longValue();
265    // The sum of cache counts of all active entry cache implementations.
266    long currentEntryCacheCount = 0;
267
268    for (EntryCache<?> entryCache : cacheOrder) {
269      // Get cache hits and counts from every active cache.
270      entryCacheHits += entryCache.getCacheHits();
271      currentEntryCacheCount += entryCache.getCacheCount();
272    }
273
274    try {
275      return EntryCacheCommon.getGenericMonitorData(
276        entryCacheHits,
277        entryCacheMisses,
278        null,
279        null,
280        currentEntryCacheCount,
281        null
282        );
283    } catch (Exception e) {
284      logger.traceException(e);
285      return new MonitorData(0);
286    }
287  }
288
289  @Override
290  public Long getCacheCount()
291  {
292    long cacheCount = 0;
293    for (EntryCache<?> entryCache : cacheOrder) {
294      cacheCount += entryCache.getCacheCount();
295    }
296    return cacheCount;
297  }
298
299  @Override
300  public String toVerboseString()
301  {
302    StringBuilder sb = new StringBuilder();
303    for (EntryCache<?> entryCache : cacheOrder)
304    {
305      String s = entryCache.toVerboseString();
306      if (s != null)
307      {
308        sb.append(s);
309      }
310    }
311    String verboseString = sb.toString();
312    return verboseString.length() > 0 ? verboseString : null;
313  }
314
315  /**
316   * Retrieves the current cache order array.
317   *
318   * @return  The current cache order array.
319   */
320  public final static EntryCache<? extends EntryCacheCfg>[] getCacheOrder()
321  {
322    return DefaultEntryCache.cacheOrder;
323  }
324
325  /**
326   * Sets the current cache order array.
327   *
328   * @param  cacheOrderMap  The current cache order array.
329   */
330  public final static void setCacheOrder(
331    SortedMap<Integer,
332    EntryCache<? extends EntryCacheCfg>> cacheOrderMap)
333  {
334    DefaultEntryCache.cacheOrder =
335      cacheOrderMap.values().toArray(new EntryCache<?>[0]);
336  }
337
338  /**
339   * Performs any processing that may be required whenever a backend
340   * is initialized for use in the Directory Server.  This method will
341   * be invoked after the backend has been initialized but before it
342   * has been put into service.
343   *
344   * @param  backend  The backend that has been initialized and is
345   *                  about to be put into service.
346   */
347  @Override
348  public void performBackendPreInitializationProcessing(Backend<?> backend)
349  {
350    // Do nothing.
351  }
352
353  /**
354   * Performs any processing that may be required whenever a backend
355   * is finalized.  This method will be invoked after the backend has
356   * been taken out of service but before it has been finalized.
357   *
358   * @param  backend  The backend that has been taken out of service
359   *                  and is about to be finalized.
360   */
361  @Override
362  public void performBackendPostFinalizationProcessing(Backend<?> backend)
363  {
364    // Do not clear any backends if the server is shutting down.
365    if (!DirectoryServer.getInstance().isShuttingDown())
366    {
367      clearBackend(backend.getBackendID());
368    }
369  }
370
371  @Override
372  public void performBackendPostInitializationProcessing(Backend<?> backend) {
373    // Nothing to do.
374  }
375
376  @Override
377  public void performBackendPreFinalizationProcessing(Backend<?> backend) {
378    // Nothing to do.
379  }
380}