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 2006-2008 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2016 ForgeRock AS.
016 */
017package org.opends.server.api;
018
019import java.util.HashSet;
020import java.util.List;
021import java.util.Set;
022import java.util.concurrent.atomic.AtomicLong;
023
024import org.forgerock.i18n.LocalizableMessage;
025import org.forgerock.i18n.slf4j.LocalizedLogger;
026import org.forgerock.opendj.config.server.ConfigException;
027import org.forgerock.opendj.server.config.server.EntryCacheCfg;
028import org.opends.server.monitors.EntryCacheMonitorProvider;
029import org.forgerock.opendj.ldap.DN;
030import org.opends.server.types.Entry;
031import org.opends.server.types.InitializationException;
032import org.opends.server.types.SearchFilter;
033
034/**
035 * This class defines the set of methods that must be implemented by a
036 * Directory Server entry cache.  Note that components accessing the
037 * entry cache must not depend on any particular behavior.  For
038 * example, if a call is made to {@code putEntry} to store an entry in
039 * the cache, there is no guarantee that immediately calling
040 * {@code getEntry} will be able to retrieve it.  There are several
041 * potential reasons for this, including:
042 * <UL>
043 *   <LI>The entry may have been deleted or replaced by another thread
044 *       between the {@code putEntry} and {@code getEntry} calls.</LI>
045 *   <LI>The entry cache may implement a purging mechanism and the
046 *       entry added may have been purged between the
047 *       {@code putEntry} and {@code getEntry} calls.</LI>
048 *   <LI>The entry cache may implement some kind of filtering
049 *       mechanism to determine which entries to store, and entries
050 *       not matching the appropriate criteria may not be stored.</LI>
051 *   <LI>The entry cache may not actually store any entries (this is
052 *       the behavior of the default cache if no implementation
053 *       specific entry cache is available).</LI>
054 * </UL>
055 *
056 * @param  <T>  The type of configuration handled by this entry
057 *              cache.
058 */
059@org.opends.server.types.PublicAPI(
060     stability=org.opends.server.types.StabilityLevel.VOLATILE,
061     mayInstantiate=false,
062     mayExtend=true,
063     mayInvoke=true,
064     notes="Entry cache methods may only be invoked by backends")
065public abstract class EntryCache<T extends EntryCacheCfg>
066{
067  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
068
069  /**
070   * The set of filters that define the entries that should be
071   * excluded from the cache.
072   */
073  private Set<SearchFilter> excludeFilters = new HashSet<>(0);
074
075  /**
076   * The set of filters that define the entries that should be
077   * included in the cache.
078   */
079  private Set<SearchFilter> includeFilters = new HashSet<>(0);
080
081  /**
082   * Arbitrary number of cache hits for monitoring.
083   */
084  protected AtomicLong cacheHits = new AtomicLong(0);
085
086  /**
087   * Arbitrary number of cache misses for monitoring.
088   */
089  protected AtomicLong cacheMisses = new AtomicLong(0);
090
091  /** The monitor associated with this entry cache. */
092  private EntryCacheMonitorProvider entryCacheMonitor;
093
094
095  /**
096   * Default constructor which is implicitly called from all entry
097   * cache implementations.
098   */
099  public EntryCache()
100  {
101    // No implementation required.
102  }
103
104  /**
105   * Initializes this entry cache implementation so that it will be
106   * available for storing and retrieving entries.
107   *
108   * @param  configuration  The configuration to use to initialize
109   *                        the entry cache.
110   *
111   * @throws  ConfigException  If there is a problem with the provided
112   *                           configuration entry that would prevent
113   *                           this entry cache from being used.
114   *
115   * @throws  InitializationException  If a problem occurs during the
116   *                                   initialization process that is
117   *                                   not related to the
118   *                                   configuration.
119   */
120  public abstract void initializeEntryCache(T configuration)
121         throws ConfigException, InitializationException;
122
123  /**
124   * Indicates whether the provided configuration is acceptable for
125   * this entry cache.  It should be possible to call this method on
126   * an uninitialized entry cache instance in order to determine
127   * whether the entry cache would be able to use the provided
128   * configuration.
129   * <BR><BR>
130   * Note that implementations which use a subclass of the provided
131   * configuration class will likely need to cast the configuration
132   * to the appropriate subclass type.
133   *
134   * @param  configuration        The entry cache configuration for
135   *                              which to make the determination.
136   * @param  unacceptableReasons  A list that may be used to hold the
137   *                              reasons that the provided
138   *                              configuration is not acceptable.
139   *
140   * @return  {@code true} if the provided configuration is acceptable
141   *          for this entry cache, or {@code false} if not.
142   */
143  public boolean isConfigurationAcceptable(
144                      EntryCacheCfg configuration,
145                      List<LocalizableMessage> unacceptableReasons)
146  {
147    // This default implementation does not perform any special
148    // validation.  It should be overridden by entry cache
149    // implementations that wish to perform more detailed validation.
150    return true;
151  }
152
153  /**
154   * Performs any necessary cleanup work (e.g., flushing all cached
155   * entries and releasing any other held resources) that should be
156   * performed when the server is to be shut down or the entry cache
157   * destroyed or replaced.
158   */
159  public abstract void finalizeEntryCache();
160
161  /**
162   * Indicates whether the entry cache currently contains the entry
163   * with the specified DN.  This method may be called without holding
164   * any locks if a point-in-time check is all that is required.
165   * Note that this method is called from @see #getEntry(DN entryDN,
166   * LockType lockType, List lockList)
167   *
168   * @param  entryDN  The DN for which to make the determination.
169   *
170   * @return  {@code true} if the entry cache currently contains the
171   *          entry with the specified DN, or {@code false} if not.
172   */
173  public abstract boolean containsEntry(DN entryDN);
174
175  /**
176   * Retrieves the entry with the specified DN from the cache.
177   *
178   * @param  entryDN   The DN of the entry to retrieve.
179   *
180   * @return  The requested entry if it is present in the cache, or
181   *          {@code null} if it is not present.
182   */
183  public abstract Entry getEntry(DN entryDN);
184
185  /**
186   * Retrieves the requested entry if it is present in the cache.
187   *
188   * @param  backendID   ID of the backend associated with the entry
189   *                     to retrieve.
190   * @param  entryID   The entry ID within the provided backend for
191   *                   the specified entry.
192   *
193   * @return  The requested entry if it is present in the cache, or
194   *          {@code null} if it is not present.
195   */
196  public Entry getEntry(String backendID, long entryID)
197  {
198    // Translate given backend/entryID pair to entryDN.
199    DN entryDN = getEntryDN(backendID, entryID);
200    if (entryDN == null)
201    {
202      // Indicate cache miss.
203      cacheMisses.getAndIncrement();
204      return null;
205    }
206    // Delegate to by DN lock and load method.
207    return getEntry(entryDN);
208  }
209
210  /**
211   * Retrieves the entry ID for the entry with the specified DN from
212   * the cache.  The caller should have already acquired a read or
213   * write lock for the entry if such protection is needed.
214   *
215   * @param  entryDN  The DN of the entry for which to retrieve the
216   *                  entry ID.
217   *
218   * @return  The entry ID for the requested entry, or -1 if it is
219   *          not present in the cache.
220   */
221  public abstract long getEntryID(DN entryDN);
222
223  /**
224   * Retrieves the entry DN for the entry with the specified ID on
225   * the specific backend from the cache.  The caller should have
226   * already acquired a read or write lock for the entry if such
227   * protection is needed.
228   * Note that this method is called from @see #getEntry(Backend
229   * backend, long entryID, LockType lockType, List lockList)
230   *
231   * @param  backendID  ID of the backend associated with the
232   *                    entry for which to retrieve the entry DN.
233   * @param  entryID    The entry ID within the provided backend
234   *                    for which to retrieve the entry DN.
235   *
236   * @return  The entry DN for the requested entry, or
237   *          {@code null} if it is not present in the cache.
238   */
239  public abstract DN getEntryDN(String backendID, long entryID);
240
241  /**
242   * Stores the provided entry in the cache.  Note that the mechanism
243   * that it uses to achieve this is implementation-dependent, and it
244   * is acceptable for the entry to not actually be stored in any
245   * cache.
246   *
247   * @param  entry      The entry to store in the cache.
248   * @param  backendID  ID of the backend with which the entry is
249   *                    associated.
250   * @param  entryID    The entry ID within the provided backend that
251   *                    uniquely identifies the specified entry.
252   */
253  public abstract void putEntry(Entry entry, String backendID, long entryID);
254
255  /**
256   * Stores the provided entry in the cache only if it does not
257   * conflict with an entry that already exists.  Note that the
258   * mechanism that it uses to achieve this is
259   * implementation-dependent, and it is acceptable for the entry to
260   * not actually be stored in any cache.  However, this method must
261   * not overwrite an existing version of the entry.
262   *
263   * @param  entry      The entry to store in the cache.
264   * @param  backendID  ID of the backend with which the entry is
265   *                    associated.
266   * @param  entryID    The entry ID within the provided backend that
267   *                    uniquely identifies the specified entry.
268   *
269   * @return  {@code false} if an existing entry or some other problem
270   *          prevented the method from completing successfully, or
271   *          {@code true} if there was no conflict and the entry was
272   *          either stored or the cache determined that this entry
273   *          should never be cached for some reason.
274   */
275  public abstract boolean putEntryIfAbsent(Entry entry,
276                                           String backendID,
277                                           long entryID);
278
279  /**
280   * Removes the specified entry from the cache.
281   *
282   * @param  entryDN  The DN of the entry to remove from the cache.
283   */
284  public abstract void removeEntry(DN entryDN);
285
286  /**
287   * Removes all entries from the cache.  The cache should still be
288   * available for future use.
289   */
290  public abstract void clear();
291
292  /**
293   * Removes all entries from the cache that are associated with the
294   * provided backend.
295   *
296   * @param  backendID  ID of the backend for which to flush the
297   *                    associated entries.
298   */
299  public abstract void clearBackend(String backendID);
300
301  /**
302   * Removes all entries from the cache that are below the provided
303   * DN.
304   *
305   * @param  baseDN  The base DN below which all entries should be
306   *                 flushed.
307   */
308  public abstract void clearSubtree(DN baseDN);
309
310  /**
311   * Attempts to react to a scenario in which it is determined that
312   * the system is running low on available memory.  In this case, the
313   * entry cache should attempt to free some memory if possible to try
314   * to avoid out of memory errors.
315   */
316  public abstract void handleLowMemory();
317
318  /**
319   * Retrieves the monitor that is associated with this entry
320   * cache.
321   *
322   * @return  The monitor that is associated with this entry
323   *          cache, or {@code null} if none has been assigned.
324   */
325  public final EntryCacheMonitorProvider getEntryCacheMonitor()
326  {
327    return entryCacheMonitor;
328  }
329
330  /**
331   * Sets the monitor for this entry cache.
332   *
333   * @param  entryCacheMonitor  The monitor for this entry cache.
334   */
335  public final void setEntryCacheMonitor(
336    EntryCacheMonitorProvider entryCacheMonitor)
337  {
338    this.entryCacheMonitor = entryCacheMonitor;
339  }
340
341  /**
342   * Retrieves a set of attributes containing monitor data that should
343   * be returned to the client if the corresponding monitor entry is
344   * requested.
345   *
346   * @return  A list of attributes containing monitor data that should
347   *          be returned to the client if the corresponding monitor
348   *          entry is requested.
349   */
350  public abstract MonitorData getMonitorData();
351
352  /**
353   * Retrieves the current number of entries stored within the cache.
354   *
355   * @return  The current number of entries stored within the cache.
356   */
357  public abstract Long getCacheCount();
358
359  /**
360   * Retrieves the current number of cache hits for this cache.
361   *
362   * @return  The current number of cache hits for this cache.
363   */
364  public long getCacheHits()
365  {
366    return cacheHits.longValue();
367  }
368
369  /**
370   * Retrieves the current number of cache misses for this cache.
371   *
372   * @return  The current number of cache misses for this cache.
373   */
374  public long getCacheMisses()
375  {
376    return cacheMisses.longValue();
377  }
378
379  /**
380   * Retrieves the set of search filters that may be used to determine
381   * whether an entry should be excluded from the cache.
382   *
383   * @return  The set of search filters that may be used to determine
384   *          whether an entry should be excluded from the cache.
385   */
386  public Set<SearchFilter> getExcludeFilters()
387  {
388    return excludeFilters;
389  }
390
391  /**
392   * Specifies the set of search filters that may be used to determine
393   * whether an entry should be excluded from the cache.
394   *
395   * @param  excludeFilters  The set of search filters that may be
396   *                         used to determine whether an entry should
397   *                         be excluded from the cache.
398   */
399  public void setExcludeFilters(Set<SearchFilter> excludeFilters)
400  {
401    if (excludeFilters == null)
402    {
403      this.excludeFilters = new HashSet<>(0);
404    }
405    else
406    {
407      this.excludeFilters = excludeFilters;
408    }
409  }
410
411  /**
412   * Retrieves the set of search filters that may be used to determine
413   * whether an entry should be included in the cache.
414   *
415   * @return  The set of search filters that may be used to determine
416   *          whether an entry should be included in the cache.
417   */
418  public Set<SearchFilter> getIncludeFilters()
419  {
420    return includeFilters;
421  }
422
423  /**
424   * Specifies the set of search filters that may be used to determine
425   * whether an entry should be included in the cache.
426   *
427   * @param  includeFilters  The set of search filters that may be
428   *                         used to determine whether an entry should
429   *                         be included in the cache.
430   */
431  public void setIncludeFilters(Set<SearchFilter> includeFilters)
432  {
433    if (includeFilters == null)
434    {
435      this.includeFilters = new HashSet<>(0);
436    }
437    else
438    {
439      this.includeFilters = includeFilters;
440    }
441  }
442
443  /**
444   * Indicates whether the current set of exclude and include filters
445   * allow caching of the specified entry.
446   *
447   * @param  entry  The entry to evaluate against exclude and include
448   *                filter sets.
449   *
450   * @return  {@code true} if current set of filters allow caching the
451   *          entry and {@code false} otherwise.
452   */
453  public boolean filtersAllowCaching(Entry entry)
454  {
455    // If there is a set of exclude filters, then make sure that the
456    // provided entry doesn't match any of them.
457    if (! excludeFilters.isEmpty())
458    {
459      for (SearchFilter f : excludeFilters)
460      {
461        try
462        {
463          if (f.matchesEntry(entry))
464          {
465            return false;
466          }
467        }
468        catch (Exception e)
469        {
470          logger.traceException(e);
471
472          // This shouldn't happen, but if it does then we can't be
473          // sure whether the entry should be excluded, so we will
474          // by default.
475          return false;
476        }
477      }
478    }
479
480    // If there is a set of include filters, then make sure that the
481    // provided entry matches at least one of them.
482    if (! includeFilters.isEmpty())
483    {
484      boolean matchFound = false;
485      for (SearchFilter f : includeFilters)
486      {
487        try
488        {
489          if (f.matchesEntry(entry))
490          {
491            matchFound = true;
492            break;
493          }
494        }
495        catch (Exception e)
496        {
497          logger.traceException(e);
498
499          // This shouldn't happen, but if it does, then
500          // just ignore it.
501        }
502      }
503
504      if (! matchFound)
505      {
506        return false;
507      }
508    }
509
510    return true;
511  }
512
513  /**
514   * Return a verbose string representation of the current cache maps. This is
515   * useful primary for debugging and diagnostic purposes such as in the entry
516   * cache unit tests.
517   * <p>
518   * This method is invoked by unit tests for debugging.
519   *
520   * @return String verbose string representation of the current cache maps in
521   *         the following format: dn:id:backend one cache entry map
522   *         representation per line or <CODE>null</CODE> if all maps are empty.
523   */
524  public abstract String toVerboseString();
525}