From 4a58a441a5b81031a86bc01b630b9e19894f4d80 Mon Sep 17 00:00:00 2001
From: abobrov <abobrov@localhost>
Date: Sun, 02 Dec 2007 17:54:15 +0000
Subject: [PATCH] - [Issue 2007]  generic mechanism for using mulitple caches. - [Issue 2049]  dsconfig is unable to configure the entry cache.

---
 opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/FileSystemEntryCacheConfiguration.xml                     |    2 
 opendj-sdk/opends/src/server/org/opends/server/extensions/DefaultEntryCache.java                                       |  306 ++++++++++-
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SoftReferenceEntryCacheTestCase.java |    5 
 opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java                                             |   13 
 opendj-sdk/opends/src/server/org/opends/server/extensions/FIFOEntryCache.java                                          |   34 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FileSystemEntryCacheTestCase.java    |    7 
 opendj-sdk/opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java                                    |   38 
 opendj-sdk/opends/src/messages/messages/config.properties                                                              |   16 
 opendj-sdk/opends/src/server/org/opends/server/extensions/EntryCacheCommon.java                                        |   48 
 opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/EntryCacheConfiguration.xml                               |   15 
 opendj-sdk/opends/src/server/org/opends/server/core/EntryCacheConfigManager.java                                       |  279 ++++++++--
 opendj-sdk/opends/src/server/org/opends/server/api/EntryCache.java                                                     |   85 ++
 opendj-sdk/opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java                                 |   36 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DefaultEntryCacheTestCase.java       |  492 +++++++++++++-----
 opendj-sdk/opends/resource/config/config.ldif                                                                          |   34 +
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FIFOEntryCacheTestCase.java          |    5 
 opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/EntryCacheMonitorProviderConfiguration.xml                |    2 
 opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml                                     |    5 
 opendj-sdk/opends/src/server/org/opends/server/monitors/EntryCacheMonitorProvider.java                                 |  108 ++-
 opendj-sdk/opends/resource/schema/02-config.ldif                                                                       |    8 
 20 files changed, 1,139 insertions(+), 399 deletions(-)

diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index ffb4bd3..7729c7d 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -487,13 +487,37 @@
 ds-cfg-listen-port: 1689
 ds-cfg-ssl-cert-nickname: server-cert
 
-dn: cn=Entry Cache,cn=config
+dn: cn=Entry Caches,cn=config
+objectClass: top
+objectClass: ds-cfg-branch
+cn: Entry Caches
+
+dn: cn=Soft Reference,cn=Entry Caches,cn=config
 objectClass: top
 objectClass: ds-cfg-entry-cache
 objectClass: ds-cfg-soft-reference-entry-cache
-cn: Entry Cache
-ds-cfg-java-class: org.opends.server.extensions.SoftReferenceEntryCache
+cn: Soft Reference
 ds-cfg-enabled: false
+ds-cfg-cache-level: 1
+ds-cfg-java-class: org.opends.server.extensions.SoftReferenceEntryCache
+
+dn: cn=FIFO,cn=Entry Caches,cn=config
+objectClass: top
+objectClass: ds-cfg-entry-cache
+objectClass: ds-cfg-fifo-entry-cache
+cn: FIFO
+ds-cfg-enabled: false
+ds-cfg-cache-level: 2
+ds-cfg-java-class: org.opends.server.extensions.FIFOEntryCache
+
+dn: cn=File System,cn=Entry Caches,cn=config
+objectClass: top
+objectClass: ds-cfg-entry-cache
+objectClass: ds-cfg-file-system-entry-cache
+cn: File System
+ds-cfg-enabled: false
+ds-cfg-cache-level: 3
+ds-cfg-java-class: org.opends.server.extensions.FileSystemEntryCache
 
 dn: cn=Extended Operations,cn=config
 objectClass: top
@@ -1171,11 +1195,11 @@
 ds-cfg-java-class: org.opends.server.monitors.ClientConnectionMonitorProvider
 ds-cfg-enabled: true
 
-dn: cn=Entry Cache,cn=Monitor Providers,cn=config
+dn: cn=Entry Caches,cn=Monitor Providers,cn=config
 objectClass: top
 objectClass: ds-cfg-monitor-provider
 objectClass: ds-cfg-entry-cache-monitor-provider
-cn: Entry Cache
+cn: Entry Caches
 ds-cfg-java-class: org.opends.server.monitors.EntryCacheMonitorProvider
 ds-cfg-enabled: true
 
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index 07e1710..cfc6b9f 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -1543,6 +1543,11 @@
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
   SINGLE-VALUE
   X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.999
+  NAME 'ds-cfg-cache-level'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDS Directory Server' )
 attributeTypes: ( 1.3.6.1.4.1.26027.1.1.315
   NAME 'ds-cfg-allow-retrieving-membership'
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
@@ -2328,7 +2333,8 @@
   STRUCTURAL
   MUST ( cn $
          ds-cfg-java-class $
-         ds-cfg-enabled )
+         ds-cfg-enabled $
+         ds-cfg-cache-level )
   X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.15
   NAME 'ds-cfg-fifo-entry-cache'
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/EntryCacheConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/EntryCacheConfiguration.xml
index 39546c8..84e34ca 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/EntryCacheConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/EntryCacheConfiguration.xml
@@ -80,4 +80,19 @@
       </ldap:attribute>
     </adm:profile>
   </adm:property>
+  <adm:property name="cache-level" mandatory="true">
+    <adm:synopsis>
+      The entry cache level which specifies this cache position in the
+      cache order if more than one instance of the cache is configured.
+    </adm:synopsis>
+    <adm:syntax>
+      <adm:integer lower-limit="1" />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-cache-level</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
 </adm:managed-object>
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/EntryCacheMonitorProviderConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/EntryCacheMonitorProviderConfiguration.xml
index 73eec0b..28ae32e 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/EntryCacheMonitorProviderConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/EntryCacheMonitorProviderConfiguration.xml
@@ -33,7 +33,7 @@
   <adm:synopsis>
     The
     <adm:user-friendly-name />
-    exposes monitor information about Directory Server entry cache
+    exposes monitor information about Directory Server entry caches
     state.
   </adm:synopsis>
   <adm:profile name="ldap">
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/FileSystemEntryCacheConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/FileSystemEntryCacheConfiguration.xml
index 4ad5179..b9c5c1c 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/FileSystemEntryCacheConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/FileSystemEntryCacheConfiguration.xml
@@ -151,7 +151,7 @@
       </ldap:attribute>
     </adm:profile>
   </adm:property>
-  <adm:property name="cache-directory" mandatory="true">
+  <adm:property name="cache-directory">
     <adm:synopsis>
       Specifies the directory in which the JE environment should store
       the cache.
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
index bcb4b58..5adcdb1 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
@@ -204,12 +204,13 @@
     </adm:profile>
   </adm:relation>
   <adm:relation name="entry-cache">
-    <adm:one-to-zero-or-one />
+    <adm:one-to-many />
     <adm:profile name="ldap">
-      <ldap:rdn-sequence>cn=Entry Cache,cn=config</ldap:rdn-sequence>
+      <ldap:rdn-sequence>cn=Entry Caches,cn=config</ldap:rdn-sequence>
     </adm:profile>
     <adm:profile name="cli">
       <cli:relation>
+        <cli:default-property name="cache-level" />
         <cli:default-property name="enabled" />
       </cli:relation>
     </adm:profile>
diff --git a/opendj-sdk/opends/src/messages/messages/config.properties b/opendj-sdk/opends/src/messages/messages/config.properties
index e3b1b98..104a964 100644
--- a/opendj-sdk/opends/src/messages/messages/config.properties
+++ b/opendj-sdk/opends/src/messages/messages/config.properties
@@ -692,11 +692,10 @@
  invalid value for configuration attribute ds-cfg-check-schema (it should be a \
  Boolean value of true or false):  %s
 SEVERE_ERR_CONFIG_ENTRYCACHE_CANNOT_INSTALL_DEFAULT_CACHE_200=An unexpected \
- error occurred that prevented the server from installing a temporary default \
- entry cache for use until the actual cache could be created from the \
- configuration:  %s
+ error occurred that prevented the server from installing its default entry \
+ cache framework:  %s
 SEVERE_WARN_CONFIG_ENTRYCACHE_NO_CONFIG_ENTRY_201=The entry cache \
- configuration entry "cn=Entry Cache,cn=config" does not exist in the \
+ configuration entry "cn=Entry Caches,cn=config" does not exist in the \
  Directory Server configuration.  No entry cache will be available until this \
  entry is created with a valid entry cache configuration
 SEVERE_ERR_CONFIG_ENTRYCACHE_CANNOT_INITIALIZE_CACHE_202=An error occurred \
@@ -705,6 +704,15 @@
  disabled
 SEVERE_ERR_CONFIG_ENTRYCACHE_CONFIG_NOT_ACCEPTABLE_203=The configuration for \
  the entry cache defined in configuration entry %s was not acceptable:  %s
+SEVERE_ERR_CONFIG_ENTRYCACHE_CONFIG_LEVEL_NOT_ACCEPTABLE_204=The configuration \
+ for the entry cache defined in configuration entry %s was not acceptable:  \
+ the entry cache level %d is already in use
+INFO_WARN_CONFIG_ENTRYCACHE_NO_MONITOR_CONFIG_ENTRY_205=The configuration for \
+ the entry cache %s monitor provider was not available. As a result this \
+ monitor provider %s will not be enabled
+INFO_WARN_CONFIG_ENTRYCACHE_MONITOR_CONFIG_DISABLED_206=The configuration for \
+ the entry cache %s monitor provider was disabled. As a result the monitor \
+ provider %s will not be enabled
 MILD_ERR_CONFIG_ENTRY_CANNOT_REMOVE_CHILD_215=An unexpected error occurred \
  while attempting to remove entry %s as a child of configuration entry %s:  %s
 SEVERE_WARN_CONFIG_SCHEMA_CANNOT_PARSE_NAME_FORM_216=Unable to parse a name \
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/EntryCache.java b/opendj-sdk/opends/src/server/org/opends/server/api/EntryCache.java
index 75dae36..aa60026 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/api/EntryCache.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/EntryCache.java
@@ -30,10 +30,10 @@
 
 
 import java.util.List;
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.locks.Lock;
-import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.opends.server.core.DirectoryServer;
@@ -45,9 +45,10 @@
 import org.opends.server.types.LockManager;
 import org.opends.server.types.SearchFilter;
 import org.opends.server.types.DebugLogLevel;
-import org.opends.server.types.Attribute;
 import org.opends.server.admin.std.server.EntryCacheCfg;
 import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.monitors.EntryCacheMonitorProvider;
+import org.opends.server.types.Attribute;
 import static org.opends.server.loggers.debug.DebugLogger.*;
 
 
@@ -70,8 +71,8 @@
  *       mechanism to determine which entries to store, and entries
  *       not matching the appropriate criteria may not be stored.</LI>
  *   <LI>The entry cache may not actually store any entries (this is
- *       the behavior of the default cache that will be used if none
- *       is configured).</LI>
+ *       the behavior of the default cache if no implementation
+ *       specific entry cache is available).</LI>
  * </UL>
  *
  * @param  <T>  The type of configuration handled by this entry
@@ -92,14 +93,12 @@
    */
   private static final DebugTracer TRACER = getTracer();
 
-
-
-  //  The set of filters that define the entries that should be
+  // The set of filters that define the entries that should be
   // excluded from the cache.
   private Set<SearchFilter> excludeFilters =
        new HashSet<SearchFilter>(0);
 
-  //  The set of filters that define the entries that should be
+  // The set of filters that define the entries that should be
   // included in the cache.
   private Set<SearchFilter> includeFilters =
        new HashSet<SearchFilter>(0);
@@ -118,6 +117,8 @@
    */
   protected AtomicLong cacheMisses = new AtomicLong(0);
 
+  // The monitor associated with this entry cache.
+  private EntryCacheMonitorProvider entryCacheMonitor = null;
 
 
   /**
@@ -253,7 +254,7 @@
 
     if (!containsEntry(entryDN)) {
       // Indicate cache miss.
-      cacheMisses.set(cacheMisses.incrementAndGet());
+      cacheMisses.getAndIncrement();
 
       return null;
     }
@@ -408,7 +409,7 @@
     DN entryDN = getEntryDN(backend, entryID);
     if (entryDN == null) {
       // Indicate cache miss.
-      cacheMisses.set(cacheMisses.incrementAndGet());
+      cacheMisses.getAndIncrement();
 
       return null;
     }
@@ -450,7 +451,7 @@
    * @return  The entry DN for the requested entry, or
    *          {@code null} if it is not present in the cache.
    */
-  protected abstract DN getEntryDN(Backend backend, long entryID);
+  public abstract DN getEntryDN(Backend backend, long entryID);
 
 
 
@@ -545,6 +546,33 @@
 
 
   /**
+   * Retrieves the monitor that is associated with this entry
+   * cache.
+   *
+   * @return  The monitor that is associated with this entry
+   *          cache, or {@code null} if none has been assigned.
+   */
+  public final EntryCacheMonitorProvider getEntryCacheMonitor()
+  {
+    return entryCacheMonitor;
+  }
+
+
+
+  /**
+   * Sets the monitor for this entry cache.
+   *
+   * @param  entryCacheMonitor  The monitor for this entry cache.
+   */
+  public final void setEntryCacheMonitor(
+    EntryCacheMonitorProvider entryCacheMonitor)
+  {
+    this.entryCacheMonitor = entryCacheMonitor;
+  }
+
+
+
+  /**
    * Retrieves a set of attributes containing monitor data that should
    * be returned to the client if the corresponding monitor entry is
    * requested.
@@ -558,6 +586,39 @@
 
 
   /**
+   * Retrieves the curent number of entries stored within the cache.
+   *
+   * @return  The current number of entries stored within the cache.
+   */
+  public abstract Long getCacheCount();
+
+
+
+  /**
+   * Retrieves the curent number of cache hits for this cache.
+   *
+   * @return  The current number of cache hits for this cache.
+   */
+  public Long getCacheHits()
+  {
+    return new Long(cacheHits.longValue());
+  }
+
+
+
+  /**
+   * Retrieves the curent number of cache misses for this cache.
+   *
+   * @return  The current number of cache misses for this cache.
+   */
+  public Long getCacheMisses()
+  {
+    return new Long(cacheMisses.longValue());
+  }
+
+
+
+  /**
    * Retrieves the maximum length of time in milliseconds to wait for
    * a lock before giving up.
    *
@@ -667,7 +728,7 @@
    * @return  {@code true} if current set of filters allow caching the
    *          entry and {@code false} otherwise.
    */
-  protected boolean filtersAllowCaching(Entry entry)
+  public boolean filtersAllowCaching(Entry entry)
   {
     // If there is a set of exclude filters, then make sure that the
     // provided entry doesn't match any of them.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
index 5b61c05..2d3ebcd 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -2771,8 +2771,8 @@
    * The DN of the entry that will hold the configuration for the Directory
    * Server entry cache.
    */
-  public static final String DN_ENTRY_CACHE_CONFIG =
-       "cn=Entry Cache," + DN_CONFIG_ROOT;
+  public static final String DN_ENTRY_CACHE_BASE =
+       "cn=Entry Caches," + DN_CONFIG_ROOT;
 
 
 
@@ -2982,6 +2982,15 @@
 
 
   /**
+   * The DN of the entry that will serve as the entry cache monitor provider
+   * configuration for all Directory Server entry cache monitors.
+   */
+  public static final String DN_ENTRY_CACHE_MONITOR_CONFIG =
+       "cn=Entry Caches," + DN_MONITOR_CONFIG_BASE;
+
+
+
+  /**
    * The DN of the entry that will serve as the base for all Directory Server
    * monitor information.
    */
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/EntryCacheConfigManager.java b/opendj-sdk/opends/src/server/org/opends/server/core/EntryCacheConfigManager.java
index 710b430..b0eede9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/EntryCacheConfigManager.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/EntryCacheConfigManager.java
@@ -31,8 +31,11 @@
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
 
 import org.opends.server.admin.ClassPropertyDefinition;
 import org.opends.server.admin.server.ConfigurationAddListener;
@@ -44,13 +47,18 @@
 import org.opends.server.admin.std.meta.EntryCacheCfgDefn;
 import org.opends.server.api.EntryCache;
 import org.opends.server.config.ConfigException;
-import org.opends.server.extensions.DefaultEntryCache;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.ConfigChangeResult;
 import org.opends.server.types.DebugLogLevel;
 import org.opends.server.types.InitializationException;
 import org.opends.server.types.ResultCode;
 import org.opends.messages.MessageBuilder;
+import org.opends.server.admin.std.server.EntryCacheMonitorProviderCfg;
+import org.opends.server.config.ConfigConstants;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.extensions.DefaultEntryCache;
+import org.opends.server.monitors.EntryCacheMonitorProvider;
+import org.opends.server.types.DN;
 
 import static org.opends.server.loggers.debug.DebugLogger.*;
 import static org.opends.server.loggers.ErrorLogger.*;
@@ -61,8 +69,8 @@
 
 /**
  * This class defines a utility that will be used to manage the configuration
- * for the Directory Server entry cache.  Only a single entry cache may be
- * defined, but if it is absent or disabled, then a default cache will be used.
+ * for the Directory Server entry cache.  The default entry cache is always
+ * enabled.
  */
 public class EntryCacheConfigManager
        implements
@@ -75,13 +83,21 @@
    */
   private static final DebugTracer TRACER = getTracer();
 
-  // The current entry cache registered in the server
-  private EntryCache _entryCache = null;
+  // The default entry cache.
+  private DefaultEntryCache _defaultEntryCache = null;
 
-  // The default entry cache to use when no entry cache has been configured
-  // or when the configured entry cache could not be initialized.
-  private EntryCache _defaultEntryCache = null;
+  // The entry cache order map sorted by the cache level.
+  private static SortedMap<Integer, EntryCache<? extends
+    EntryCacheCfg>> cacheOrderMap = new TreeMap<Integer,
+    EntryCache<? extends EntryCacheCfg>>();
 
+  // The entry cache name to level map.
+  private static HashMap<String, Integer>
+    cacheNameToLevelMap = new HashMap<String, Integer>();
+
+  // Global entry cache monitor provider name.
+  private static final String
+    DEFAULT_ENTRY_CACHE_MONITOR_PROVIDER = "Entry Caches";
 
   /**
    * Creates a new instance of this entry cache config manager.
@@ -127,8 +143,8 @@
   /**
    * Initializes the configuration associated with the Directory Server entry
    * cache.  This should only be called at Directory Server startup.  If an
-   * error occurs, then a message will be logged and the default entry cache
-   * will be installed.
+   * error occurs, then a message will be logged for each entry cache that is
+   * failed to initialize.
    *
    * @throws  ConfigException  If a configuration problem causes the entry
    *                           cache initialization process to fail.
@@ -152,34 +168,61 @@
     rootConfiguration.addEntryCacheAddListener(this);
     rootConfiguration.addEntryCacheDeleteListener(this);
 
-    // If the entry cache configuration is not present then keep the
-    // default entry cache already installed.
-    if (!rootConfiguration.hasEntryCache())
+    // Get the base entry cache configuration entry.
+    ConfigEntry entryCacheBase;
+    try {
+      DN configEntryDN = DN.decode(ConfigConstants.DN_ENTRY_CACHE_BASE);
+      entryCacheBase   = DirectoryServer.getConfigEntry(configEntryDN);
+    } catch (Exception e) {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      logError(WARN_CONFIG_ENTRYCACHE_NO_CONFIG_ENTRY.get());
+      return;
+    }
+
+    // If the configuration base entry is null, then assume it doesn't exist.
+    // At least that entry must exist in the configuration, even if there are
+    // no entry cache defined below it.
+    if (entryCacheBase == null)
     {
       logError(WARN_CONFIG_ENTRYCACHE_NO_CONFIG_ENTRY.get());
       return;
     }
 
-    // Get the entry cache configuration.
-    EntryCacheCfg configuration = rootConfiguration.getEntryCache();
-
-    // At this point, we have a configuration entry. Register a change
-    // listener with it so we can be notified of changes to it over time.
-    configuration.addChangeListener(this);
-
-    // Initialize the entry cache.
-    if (configuration.isEnabled())
+    // Initialize every entry cache configured.
+    for (String cacheName : rootConfiguration.listEntryCaches())
     {
-      // Load the entry cache implementation class and install the entry
-      // cache with the server.
-      String className = configuration.getJavaClass();
-      try
-      {
-        loadAndInstallEntryCache (className);
+      // Get the entry cache configuration.
+      EntryCacheCfg configuration = rootConfiguration.getEntryCache(cacheName);
+
+      // At this point, we have a configuration entry. Register a change
+      // listener with it so we can be notified of changes to it over time.
+      configuration.addChangeListener(this);
+
+      // Check if there is another entry cache installed at the same level.
+      if (!cacheOrderMap.isEmpty()) {
+        if (cacheOrderMap.containsKey(configuration.getCacheLevel())) {
+          // Log error and skip this cache.
+          logError(ERR_CONFIG_ENTRYCACHE_CONFIG_LEVEL_NOT_ACCEPTABLE.get(
+            String.valueOf(configuration.dn()),
+            configuration.getCacheLevel()));
+          continue;
+        }
       }
-      catch (InitializationException ie)
-      {
-        logError(ie.getMessageObject());
+
+      // Initialize the entry cache.
+      if (configuration.isEnabled()) {
+        // Load the entry cache implementation class and install the entry
+        // cache with the server.
+        String className = configuration.getJavaClass();
+        try {
+          loadAndInstallEntryCache(className, configuration);
+        } catch (InitializationException ie) {
+          logError(ie.getMessageObject());
+        }
       }
     }
   }
@@ -207,6 +250,23 @@
       status = false;
     }
 
+    if (!cacheOrderMap.isEmpty() && !cacheNameToLevelMap.isEmpty() &&
+      (cacheNameToLevelMap.get(
+       configuration.dn().toNormalizedString()) != null)) {
+      int currentCacheLevel = cacheNameToLevelMap.get(
+        configuration.dn().toNormalizedString());
+
+      // Check if there any existing cache at the same level.
+      if ((currentCacheLevel != configuration.getCacheLevel()) &&
+        (cacheOrderMap.containsKey(configuration.getCacheLevel()))) {
+        unacceptableReasons.add(
+          ERR_CONFIG_ENTRYCACHE_CONFIG_LEVEL_NOT_ACCEPTABLE.get(
+            String.valueOf(configuration.dn()),
+            configuration.getCacheLevel()));
+        status = false;
+      }
+    }
+
     return status;
   }
 
@@ -218,34 +278,64 @@
       EntryCacheCfg configuration
       )
   {
+    EntryCache<? extends EntryCacheCfg> entryCache = null;
+
+    // If we this entry cache is already installed and active it
+    // should be present in the cache maps, if so use it.
+    if (!cacheOrderMap.isEmpty() && !cacheNameToLevelMap.isEmpty() &&
+      (cacheNameToLevelMap.get(
+       configuration.dn().toNormalizedString()) != null)) {
+      int currentCacheLevel = cacheNameToLevelMap.get(
+        configuration.dn().toNormalizedString());
+      entryCache = cacheOrderMap.get(currentCacheLevel);
+
+      // Check if the existing cache just shifted its level.
+      if (currentCacheLevel != configuration.getCacheLevel()) {
+        // Update the maps then.
+        cacheOrderMap.remove(currentCacheLevel);
+        cacheOrderMap.put(configuration.getCacheLevel(), entryCache);
+        cacheNameToLevelMap.put(configuration.dn().toNormalizedString(),
+          configuration.getCacheLevel());
+      }
+    }
+
     // Returned result.
     ConfigChangeResult changeResult = new ConfigChangeResult(
         ResultCode.SUCCESS, false, new ArrayList<Message>()
         );
 
-    // If the new configuration has the entry cache disabled, then install
-    // the default entry cache with the server.
-    if (! configuration.isEnabled())
+    // If an entry cache was installed then remove it.
+    if (!configuration.isEnabled())
     {
-      DirectoryServer.setEntryCache (_defaultEntryCache);
-
-      // If an entry cache was installed then clean it.
-      if (_entryCache != null)
+      configuration.getCacheLevel();
+      if (entryCache != null)
       {
-        _entryCache.finalizeEntryCache();
-        _entryCache = null;
+        EntryCacheMonitorProvider monitor = entryCache.getEntryCacheMonitor();
+        if (monitor != null)
+        {
+          String instanceName = toLowerCase(monitor.getMonitorInstanceName());
+          DirectoryServer.deregisterMonitorProvider(instanceName);
+          monitor.finalizeMonitorProvider();
+          entryCache.setEntryCacheMonitor(null);
+        }
+        entryCache.finalizeEntryCache();
+        cacheOrderMap.remove(configuration.getCacheLevel());
+        entryCache = null;
       }
       return changeResult;
     }
 
+    // Push any changes made to the cache order map.
+    _defaultEntryCache.setCacheOrder(cacheOrderMap);
+
     // At this point, new configuration is enabled...
     // If the current entry cache is already enabled then we don't do
     // anything unless the class has changed in which case we should
     // indicate that administrative action is required.
     String newClassName = configuration.getJavaClass();
-    if (_entryCache !=null)
+    if ( entryCache != null)
     {
-      String curClassName = _entryCache.getClass().getName();
+      String curClassName = entryCache.getClass().getName();
       boolean classIsNew = (! newClassName.equals (curClassName));
       if (classIsNew)
       {
@@ -258,7 +348,7 @@
     // Instantiate the new class and initalize it.
     try
     {
-      loadAndInstallEntryCache (newClassName);
+      loadAndInstallEntryCache (newClassName, configuration);
     }
     catch (InitializationException ie)
     {
@@ -282,6 +372,18 @@
     // returned status -- all is fine by default
     boolean status = true;
 
+    // Check if there is another entry cache installed at the same level.
+    if (!cacheOrderMap.isEmpty()) {
+      if (cacheOrderMap.containsKey(configuration.getCacheLevel())) {
+        unacceptableReasons.add(
+          ERR_CONFIG_ENTRYCACHE_CONFIG_LEVEL_NOT_ACCEPTABLE.get(
+            String.valueOf(configuration.dn()),
+            configuration.getCacheLevel()));
+        status = false;
+        return status;
+      }
+    }
+
     if (configuration.isEnabled())
     {
       // Get the name of the class and make sure we can instantiate it as
@@ -325,7 +427,7 @@
       String className = configuration.getJavaClass();
       try
       {
-        loadAndInstallEntryCache (className);
+        loadAndInstallEntryCache (className, configuration);
       }
       catch (InitializationException ie)
       {
@@ -361,18 +463,38 @@
       EntryCacheCfg configuration
       )
   {
+    EntryCache<? extends EntryCacheCfg> entryCache = null;
+
+    // If we this entry cache is already installed and active it
+    // should be present in the current cache order map, use it.
+    if (!cacheOrderMap.isEmpty()) {
+      entryCache = cacheOrderMap.get(configuration.getCacheLevel());
+    }
+
     // Returned result.
     ConfigChangeResult changeResult = new ConfigChangeResult(
         ResultCode.SUCCESS, false, new ArrayList<Message>()
         );
 
-    // If the entry cache was installed then replace it with the
-    // default entry cache, and clean it.
-    if (_entryCache != null)
+    // If the entry cache was installed then remove it.
+    if (entryCache != null)
     {
-      DirectoryServer.setEntryCache (_defaultEntryCache);
-      _entryCache.finalizeEntryCache();
-      _entryCache = null;
+      EntryCacheMonitorProvider monitor = entryCache.getEntryCacheMonitor();
+      if (monitor != null)
+      {
+        String instanceName = toLowerCase(monitor.getMonitorInstanceName());
+        DirectoryServer.deregisterMonitorProvider(instanceName);
+        monitor.finalizeMonitorProvider();
+        entryCache.setEntryCacheMonitor(null);
+      }
+      entryCache.finalizeEntryCache();
+      cacheOrderMap.remove(configuration.getCacheLevel());
+      cacheNameToLevelMap.remove(configuration.dn().toNormalizedString());
+
+      // Push any changes made to the cache order map.
+      _defaultEntryCache.setCacheOrder(cacheOrderMap);
+
+      entryCache = null;
     }
 
     return changeResult;
@@ -394,34 +516,47 @@
    *                                   to initialize the entry cache.
    */
   private void loadAndInstallEntryCache(
-    String        className
+    String        className,
+    EntryCacheCfg configuration
     )
     throws InitializationException
   {
-    EntryCacheCfg configuration;
-
     // Get the root configuration object.
     ServerManagementContext managementContext =
       ServerManagementContext.getInstance();
     RootCfg rootConfiguration =
       managementContext.getRootConfiguration();
 
-    // Get the entry cache configuration.
-    try {
-      configuration = rootConfiguration.getEntryCache();
-    } catch (ConfigException ce) {
-      Message message = ERR_CONFIG_ENTRYCACHE_CANNOT_INITIALIZE_CACHE.get(
-        className, (ce.getCause() != null ? ce.getCause().getMessage() :
-          stackTraceToSingleLineString(ce)));
-      throw new InitializationException(message, ce);
-    }
-
     // Load the entry cache class...
-    EntryCache entryCache = loadEntryCache (className, configuration, true);
+    EntryCache<? extends EntryCacheCfg> entryCache =
+      loadEntryCache (className, configuration, true);
 
     // ... and install the entry cache in the server.
-    DirectoryServer.setEntryCache(entryCache);
-    _entryCache = entryCache;
+
+    // Add this entry cache to the current cache config maps.
+    cacheOrderMap.put(configuration.getCacheLevel(), entryCache);
+    cacheNameToLevelMap.put(configuration.dn().toNormalizedString(),
+      configuration.getCacheLevel());
+
+    // Push any changes made to the cache order map.
+    _defaultEntryCache.setCacheOrder(cacheOrderMap);
+
+    // Install and register the monitor for this cache.
+    EntryCacheMonitorProvider monitor = new EntryCacheMonitorProvider(
+        configuration.definition().getUserFriendlyName().toString(),
+        entryCache);
+    try {
+      monitor.initializeMonitorProvider((EntryCacheMonitorProviderCfg)
+        rootConfiguration.getMonitorProvider(
+        DEFAULT_ENTRY_CACHE_MONITOR_PROVIDER));
+    } catch (ConfigException ce) {
+      // ConfigException here means that either the entry cache monitor
+      // config entry is not present or the monitor is not enabled. In
+      // either case that means no monitor provider for this cache.
+      return;
+    }
+    entryCache.setEntryCacheMonitor(monitor);
+    DirectoryServer.registerMonitorProvider(monitor);
   }
 
 
@@ -448,6 +583,14 @@
     )
     throws InitializationException
   {
+    EntryCache entryCache = null;
+
+    // If we this entry cache is already installed and active it
+    // should be present in the current cache order map, use it.
+    if (!cacheOrderMap.isEmpty()) {
+      entryCache = cacheOrderMap.get(configuration.getCacheLevel());
+    }
+
     try
     {
       EntryCacheCfgDefn                   definition;
@@ -461,10 +604,10 @@
 
       // If there is some entry cache instance already initialized work with
       // it instead of creating a new one unless explicit init is requested.
-      if (initialize || (_entryCache == null)) {
+      if (initialize || (entryCache == null)) {
         cache = (EntryCache<? extends EntryCacheCfg>) cacheClass.newInstance();
       } else {
-        cache = (EntryCache<? extends EntryCacheCfg>) _entryCache;
+        cache = (EntryCache<? extends EntryCacheCfg>) entryCache;
       }
 
       if (initialize)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/DefaultEntryCache.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/DefaultEntryCache.java
index 4ba492d..c7912cf 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/DefaultEntryCache.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/DefaultEntryCache.java
@@ -22,42 +22,59 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
+import java.lang.reflect.Method;
 import org.opends.messages.Message;
 
 
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.SortedMap;
+import java.util.concurrent.locks.Lock;
 
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.std.server.EntryCacheCfg;
 import org.opends.server.api.Backend;
 import org.opends.server.api.EntryCache;
 import org.opends.server.config.ConfigException;
+import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.ConfigChangeResult;
 import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
 import org.opends.server.types.Entry;
 import org.opends.server.types.InitializationException;
+import org.opends.server.types.LockType;
 import org.opends.server.types.ResultCode;
 
-
+import static org.opends.server.loggers.debug.DebugLogger.*;
 
 /**
- * This class defines the default entry cache that will be used in the server if
- * none is configured.  It does not actually store any entries, so all calls to
- * <CODE>getEntry</CODE> will return <CODE>null</CODE>, and all calls to
- * <CODE>putEntry</CODE> will return immediately without doing anything.
+ * This class defines the default entry cache which acts as an arbiter for
+ * every entry cache implemenation configured and installed withhin the
+ * Directory Server or acts an an empty cache if no implementation specific
+ * entry cache is configured.  It does not actually store any entries, so
+ * all calls to the entry cache public API are routed to underlying entry
+ * cache according to the current configuration order and preferences.
  */
 public class DefaultEntryCache
        extends EntryCache<EntryCacheCfg>
        implements ConfigurationChangeListener<EntryCacheCfg>
 {
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
 
 
+  // The entry cache order array reflects all currently configured and
+  // active entry cache implementations in cache level specific order.
+  private static EntryCache<? extends EntryCacheCfg>[] cacheOrder =
+    new EntryCache<?>[0];
+
 
   /**
    * Creates a new instance of this default entry cache.
@@ -65,11 +82,9 @@
   public DefaultEntryCache()
   {
     super();
-
   }
 
 
-
   /**
    * {@inheritDoc}
    */
@@ -80,7 +95,6 @@
   }
 
 
-
   /**
    * {@inheritDoc}
    */
@@ -90,25 +104,98 @@
   }
 
 
-
   /**
    * {@inheritDoc}
    */
   public boolean containsEntry(DN entryDN)
   {
-    // This implementation does not store any entries.
+    if (entryDN == null) {
+      return false;
+    }
+
+    for (EntryCache entryCache : cacheOrder) {
+      if (entryCache.containsEntry(entryDN)) {
+        return true;
+      }
+    }
+
     return false;
   }
 
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Entry getEntry(DN entryDN,
+                        LockType lockType,
+                        List<Lock> lockList)
+  {
+    Entry entry = null;
+
+    for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder) {
+      entry = entryCache.getEntry(entryDN, lockType, lockList);
+      if (entry != null) {
+        break;
+      }
+    }
+
+    // Indicate global cache miss.
+    if ((entry == null) && (cacheOrder.length != 0)) {
+      cacheMisses.getAndIncrement();
+    }
+
+    return entry;
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Entry getEntry(Backend backend, long entryID,
+                                 LockType lockType,
+                                 List<Lock> lockList)
+  {
+    Entry entry = null;
+
+    for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder) {
+      entry = entryCache.getEntry(backend, entryID, lockType,
+          lockList);
+      if (entry != null) {
+        break;
+      }
+    }
+
+    // Indicate global cache miss.
+    if ((entry == null) && (cacheOrder.length != 0)) {
+      cacheMisses.getAndIncrement();
+    }
+
+    return entry;
+  }
+
 
   /**
    * {@inheritDoc}
    */
   public Entry getEntry(DN entryDN)
   {
-    // This implementation does not store any entries.
-    return null;
+    Entry entry = null;
+
+    for (EntryCache entryCache : cacheOrder) {
+      entry = entryCache.getEntry(entryDN);
+      if (entry != null) {
+        break;
+      }
+    }
+
+    // Indicate global cache miss.
+    if ((entry == null) && (cacheOrder.length != 0)) {
+      cacheMisses.getAndIncrement();
+    }
+
+    return entry;
   }
 
 
@@ -118,8 +205,16 @@
    */
   public long getEntryID(DN entryDN)
   {
-    // This implementation does not store any entries.
-    return -1;
+    long entryID = -1;
+
+    for (EntryCache entryCache : cacheOrder) {
+      entryID = entryCache.getEntryID(entryDN);
+      if (entryID != -1) {
+        break;
+      }
+    }
+
+    return entryID;
   }
 
 
@@ -127,10 +222,18 @@
   /**
    * {@inheritDoc}
    */
-  protected DN getEntryDN(Backend backend, long entryID)
+  public DN getEntryDN(Backend backend, long entryID)
   {
-    // This implementation does not store any entries.
-    return null;
+    DN entryDN = null;
+
+    for (EntryCache entryCache : cacheOrder) {
+      entryDN = entryCache.getEntryDN(backend, entryID);
+      if (entryDN != null) {
+        break;
+      }
+    }
+
+    return entryDN;
   }
 
 
@@ -140,7 +243,14 @@
    */
   public void putEntry(Entry entry, Backend backend, long entryID)
   {
-    // This implementation does not store entries.
+    for (EntryCache entryCache : cacheOrder) {
+      // The first cache in the order which can take this entry
+      // gets it.
+      if (entryCache.filtersAllowCaching(entry)) {
+        entryCache.putEntry(entry, backend, entryID);
+        break;
+      }
+    }
   }
 
 
@@ -150,9 +260,15 @@
    */
   public boolean putEntryIfAbsent(Entry entry, Backend backend, long entryID)
   {
-    // This implementation does not store entries, so we will never have a
-    // conflict.
-    return true;
+    for (EntryCache entryCache : cacheOrder) {
+      // The first cache in the order which can take this entry
+      // gets it.
+      if (entryCache.filtersAllowCaching(entry)) {
+        return entryCache.putEntryIfAbsent(entry, backend, entryID);
+      }
+    }
+
+    return false;
   }
 
 
@@ -162,7 +278,12 @@
    */
   public void removeEntry(DN entryDN)
   {
-    // This implementation does not store entries.
+    for (EntryCache entryCache : cacheOrder) {
+      if (entryCache.containsEntry(entryDN)) {
+        entryCache.removeEntry(entryDN);
+        break;
+      }
+    }
   }
 
 
@@ -172,7 +293,9 @@
    */
   public void clear()
   {
-    // This implementation does not store entries.
+    for (EntryCache entryCache : cacheOrder) {
+      entryCache.clear();
+    }
   }
 
 
@@ -182,7 +305,9 @@
    */
   public void clearBackend(Backend backend)
   {
-    // This implementation does not store entries.
+    for (EntryCache entryCache : cacheOrder) {
+      entryCache.clearBackend(backend);
+    }
   }
 
 
@@ -192,7 +317,9 @@
    */
   public void clearSubtree(DN baseDN)
   {
-    // This implementation does not store entries.
+    for (EntryCache entryCache : cacheOrder) {
+      entryCache.clearSubtree(baseDN);
+    }
   }
 
 
@@ -202,8 +329,9 @@
    */
   public void handleLowMemory()
   {
-    // This implementation does not store entries, so there are no resources
-    // that it can free.
+    for (EntryCache entryCache : cacheOrder) {
+      entryCache.handleLowMemory();
+    }
   }
 
 
@@ -230,7 +358,6 @@
       )
   {
     // No implementation required.
-
     ConfigChangeResult changeResult = new ConfigChangeResult(
         ResultCode.SUCCESS, false, new ArrayList<Message>()
         );
@@ -245,9 +372,124 @@
    */
   public ArrayList<Attribute> getMonitorData()
   {
-    // This implementation does not store entries,
-    // so there is no monitoring data to provide.
-    return new ArrayList<Attribute>();
+    ArrayList<Attribute> attrs = new ArrayList<Attribute>();
+
+    // The sum of cache hits of all active entry cache
+    // implementations.
+    Long entryCacheHits = new Long(0);
+    // Common for all active entry cache implementations.
+    Long entryCacheMisses = new Long(cacheMisses.longValue());
+    // The sum of cache counts of all active entry cache
+    // implementations.
+    Long currentEntryCacheCount = new Long(0);
+
+    for (EntryCache entryCache : cacheOrder) {
+      // Get cache hits and counts from every active cache.
+      entryCacheHits += entryCache.getCacheHits();
+      currentEntryCacheCount += entryCache.getCacheCount();
+    }
+
+    try {
+      attrs = EntryCacheCommon.getGenericMonitorData(
+        entryCacheHits,
+        entryCacheMisses,
+        null,
+        null,
+        currentEntryCacheCount,
+        null
+        );
+    } catch (Exception e) {
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+    }
+
+    return attrs;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public Long getCacheCount()
+  {
+    Long cacheCount = new Long(0);
+
+    for (EntryCache entryCache : cacheOrder) {
+      cacheCount += entryCache.getCacheCount();
+    }
+
+    return cacheCount;
+  }
+
+
+
+  /**
+   * Return a verbose string representation of the current cache maps.
+   * This is useful primary for debugging and diagnostic purposes such
+   * as in the entry cache unit tests.
+   * @return String verbose string representation of the current cache
+   *                maps in the following format: dn:id:backend
+   *                one cache entry map representation per line
+   *                or <CODE>null</CODE> if all maps are empty.
+   */
+  private String toVerboseString()
+  {
+    String verboseString = new String();
+    StringBuilder sb = new StringBuilder();
+
+    for (EntryCache entryCache : cacheOrder) {
+      final Method[] cacheMethods =
+        entryCache.getClass().getDeclaredMethods();
+      for (int i = 0; i < cacheMethods.length; ++i) {
+        if (cacheMethods[i].getName().equals("toVerboseString")) {
+          cacheMethods[i].setAccessible(true);
+          try {
+            Object cacheVerboseString =
+              cacheMethods[i].invoke(entryCache, (Object[]) null);
+            if (cacheVerboseString != null) {
+              sb.append((String) cacheVerboseString);
+            }
+          } catch (Exception e) {
+            if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, e);
+            }
+          }
+        }
+      }
+    }
+
+    verboseString = sb.toString();
+
+    return (verboseString.length() > 0 ? verboseString : null);
+  }
+
+
+
+  /**
+   * Retrieves the current cache order array.
+   *
+   * @return  The current cache order array.
+   */
+  public final EntryCache<? extends EntryCacheCfg>[] getCacheOrder()
+  {
+    return this.cacheOrder;
+  }
+
+
+
+  /**
+   * Sets the current cache order array.
+   *
+   * @param  cacheOrderMap  The current cache order array.
+   */
+  public final void setCacheOrder(
+    SortedMap<Integer,
+    EntryCache<? extends EntryCacheCfg>> cacheOrderMap)
+  {
+    this.cacheOrder =
+      cacheOrderMap.values().toArray(new EntryCache<?>[0]);
   }
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/EntryCacheCommon.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/EntryCacheCommon.java
index eac3bbb..49edcdf 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/EntryCacheCommon.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/EntryCacheCommon.java
@@ -381,7 +381,7 @@
   {
     ArrayList<Attribute> attrs = new ArrayList<Attribute>();
 
-    if ((cacheHits != null) && (cacheMisses != null)) {
+    if (cacheHits != null) {
       AttributeType hitsAttrType =
         DirectoryServer.getDefaultAttributeType("entryCacheHits");
       LinkedHashSet<AttributeValue> hitsValues =
@@ -390,29 +390,31 @@
         cacheHits.toString()));
       attrs.add(new Attribute(hitsAttrType, "entryCacheHits",
         hitsValues));
+      // Cache misses is required to get cache tries and hit ratio.
+      if (cacheMisses != null) {
+        AttributeType triesAttrType =
+          DirectoryServer.getDefaultAttributeType("entryCacheTries");
+        LinkedHashSet<AttributeValue> triesValues =
+          new LinkedHashSet<AttributeValue>();
+        Long cacheTries = cacheHits + cacheMisses;
+        triesValues.add(new AttributeValue(triesAttrType,
+          cacheTries.toString()));
+        attrs.add(new Attribute(triesAttrType, "entryCacheTries",
+          triesValues));
 
-      AttributeType triesAttrType =
-        DirectoryServer.getDefaultAttributeType("entryCacheTries");
-      LinkedHashSet<AttributeValue> triesValues =
-        new LinkedHashSet<AttributeValue>();
-      Long cacheTries = cacheHits + cacheMisses;
-      triesValues.add(new AttributeValue(triesAttrType,
-        cacheTries.toString()));
-      attrs.add(new Attribute(triesAttrType, "entryCacheTries",
-        triesValues));
-
-      AttributeType hitRatioAttrType =
-        DirectoryServer.getDefaultAttributeType("entryCacheHitRatio");
-      LinkedHashSet<AttributeValue> hitRatioValues =
-        new LinkedHashSet<AttributeValue>();
-      Double hitRatioRaw = cacheTries > 0 ?
-        cacheHits.doubleValue() / cacheTries.doubleValue() :
-        cacheHits.doubleValue() / 1;
-      Double hitRatio = hitRatioRaw * 100D;
-      hitRatioValues.add(new AttributeValue(hitRatioAttrType,
-        Long.toString(hitRatio.longValue())));
-      attrs.add(new Attribute(hitRatioAttrType, "entryCacheHitRatio",
-        hitRatioValues));
+        AttributeType hitRatioAttrType =
+          DirectoryServer.getDefaultAttributeType("entryCacheHitRatio");
+        LinkedHashSet<AttributeValue> hitRatioValues =
+          new LinkedHashSet<AttributeValue>();
+        Double hitRatioRaw = cacheTries > 0 ?
+          cacheHits.doubleValue() / cacheTries.doubleValue() :
+          cacheHits.doubleValue() / 1;
+        Double hitRatio = hitRatioRaw * 100D;
+        hitRatioValues.add(new AttributeValue(hitRatioAttrType,
+          Long.toString(hitRatio.longValue())));
+        attrs.add(new Attribute(hitRatioAttrType, "entryCacheHitRatio",
+          hitRatioValues));
+      }
     }
 
     if (cacheSize != null) {
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/FIFOEntryCache.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/FIFOEntryCache.java
index 63fb87e..2508aba 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/FIFOEntryCache.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/FIFOEntryCache.java
@@ -213,6 +213,10 @@
    */
   public boolean containsEntry(DN entryDN)
   {
+    if (entryDN == null) {
+      return false;
+    }
+
     // Indicate whether the DN map contains the specified DN.
     return dnMap.containsKey(entryDN);
   }
@@ -229,13 +233,13 @@
     if (e == null)
     {
       // Indicate cache miss.
-      cacheMisses.set(cacheMisses.incrementAndGet());
+      cacheMisses.getAndIncrement();
       return null;
     }
     else
     {
       // Indicate cache hit.
-      cacheHits.set(cacheHits.incrementAndGet());
+      cacheHits.getAndIncrement();
       return e.getEntry();
     }
   }
@@ -264,7 +268,7 @@
   /**
    * {@inheritDoc}
    */
-  protected DN getEntryDN(Backend backend, long entryID)
+  public DN getEntryDN(Backend backend, long entryID)
   {
     // Locate specific backend map and return the entry DN by ID.
     HashMap<Long,CacheEntry> backendMap = idMap.get(backend);
@@ -284,11 +288,6 @@
    */
   public void putEntry(Entry entry, Backend backend, long entryID)
   {
-    // Check exclude and include filters first.
-    if (!filtersAllowCaching(entry)) {
-      return;
-    }
-
     // Create the cache entry based on the provided information.
     CacheEntry cacheEntry = new CacheEntry(entry, backend, entryID);
 
@@ -400,11 +399,6 @@
    */
   public boolean putEntryIfAbsent(Entry entry, Backend backend, long entryID)
   {
-    // Check exclude and include filters first.
-    if (!filtersAllowCaching(entry)) {
-      return true;
-    }
-
     // Create the cache entry based on the provided information.
     CacheEntry cacheEntry = new CacheEntry(entry, backend, entryID);
 
@@ -1004,7 +998,9 @@
     try {
       attrs = EntryCacheCommon.getGenericMonitorData(
         new Long(cacheHits.longValue()),
-        new Long(cacheMisses.longValue()),
+        // If cache misses is maintained by default cache
+        // get it from there and if not point to itself.
+        DirectoryServer.getEntryCache().getCacheMisses(),
         null,
         new Long(maxAllowedMemory),
         new Long(dnMap.size()),
@@ -1024,6 +1020,16 @@
 
 
   /**
+   * {@inheritDoc}
+   */
+  public Long getCacheCount()
+  {
+    return new Long(dnMap.size());
+  }
+
+
+
+  /**
    * Return a verbose string representation of the current cache maps.
    * This is useful primary for debugging and diagnostic purposes such
    * as in the entry cache unit tests.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java
index 991f644..4928f94 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/FileSystemEntryCache.java
@@ -78,8 +78,8 @@
 import org.opends.server.types.FilePermission;
 import org.opends.server.types.DebugLogLevel;
 import org.opends.server.types.OpenDsException;
-import org.opends.server.types.Attribute;
 import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.Attribute;
 import org.opends.server.util.ServerConstants;
 
 import static org.opends.server.loggers.debug.DebugLogger.*;
@@ -607,6 +607,10 @@
    */
   public boolean containsEntry(DN entryDN)
   {
+    if (entryDN == null) {
+      return false;
+    }
+
     // Indicate whether the DN map contains the specified DN.
     boolean containsEntry = false;
     cacheReadLock.lock();
@@ -631,10 +635,10 @@
       if (dnMap.get(entryDN) != null) {
         entry = getEntryFromDB(entryDN);
         // Indicate cache hit.
-        cacheHits.set(cacheHits.incrementAndGet());
+        cacheHits.getAndIncrement();
       } else {
         // Indicate cache miss.
-        cacheMisses.set(cacheMisses.incrementAndGet());
+        cacheMisses.getAndIncrement();
       }
     } finally {
       cacheReadLock.unlock();
@@ -662,7 +666,7 @@
   /**
    * {@inheritDoc}
    */
-  protected DN getEntryDN(Backend backend, long entryID) {
+  public DN getEntryDN(Backend backend, long entryID) {
 
     DN entryDN = null;
     cacheReadLock.lock();
@@ -684,13 +688,8 @@
   /**
    * {@inheritDoc}
    */
-  public void putEntry(Entry entry, Backend backend, long entryID) {
-
-    // Check exclude and include filters first.
-    if (!filtersAllowCaching(entry)) {
-      return;
-    }
-
+  public void putEntry(Entry entry, Backend backend, long entryID)
+  {
     // Obtain a lock on the cache.  If this fails, then don't do anything.
     try {
       if (!cacheWriteLock.tryLock(getLockTimeout(), TimeUnit.MILLISECONDS)) {
@@ -714,11 +713,6 @@
    */
   public boolean putEntryIfAbsent(Entry entry, Backend backend, long entryID)
   {
-    // Check exclude and include filters first.
-    if (!filtersAllowCaching(entry)) {
-      return true;
-    }
-
     try {
       // Obtain a lock on the cache.  If this fails, then don't do anything.
       if (! cacheWriteLock.tryLock(getLockTimeout(), TimeUnit.MILLISECONDS)) {
@@ -1350,7 +1344,9 @@
     try {
       attrs = EntryCacheCommon.getGenericMonitorData(
         new Long(cacheHits.longValue()),
-        new Long(cacheMisses.longValue()),
+        // If cache misses is maintained by default cache
+        // get it from there and if not point to itself.
+        DirectoryServer.getEntryCache().getCacheMisses(),
         new Long(entryCacheEnv.getStats(
           entryCacheEnvStatsConfig).getTotalLogSize()),
         new Long(maxAllowedMemory),
@@ -1369,6 +1365,14 @@
   }
 
   /**
+   * {@inheritDoc}
+   */
+  public Long getCacheCount()
+  {
+    return new Long(dnMap.size());
+  }
+
+  /**
    * Retrieves and decodes the entry with the specified DN from JE backend db.
    *
    * @param  entryDN   The DN of the entry to retrieve.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java
index f35c776..209b180 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/SoftReferenceEntryCache.java
@@ -195,6 +195,10 @@
    */
   public boolean containsEntry(DN entryDN)
   {
+    if (entryDN == null) {
+      return false;
+    }
+
     // Indicate whether the DN map contains the specified DN.
     return dnMap.containsKey(entryDN);
   }
@@ -210,7 +214,7 @@
     if (ref == null)
     {
       // Indicate cache miss.
-      cacheMisses.set(cacheMisses.incrementAndGet());
+      cacheMisses.getAndIncrement();
       return null;
     }
     else
@@ -219,13 +223,13 @@
       if (cacheEntry == null)
       {
         // Indicate cache miss.
-        cacheMisses.set(cacheMisses.incrementAndGet());
+        cacheMisses.getAndIncrement();
         return null;
       }
       else
       {
         // Indicate cache hit.
-        cacheHits.set(cacheHits.incrementAndGet());
+        cacheHits.getAndIncrement();
         return cacheEntry.getEntry();
       }
     }
@@ -262,7 +266,7 @@
   /**
    * {@inheritDoc}
    */
-  protected DN getEntryDN(Backend backend, long entryID)
+  public DN getEntryDN(Backend backend, long entryID)
   {
     // Locate specific backend map and return the entry DN by ID.
     ConcurrentHashMap<Long,SoftReference<CacheEntry>>
@@ -286,11 +290,6 @@
    */
   public void putEntry(Entry entry, Backend backend, long entryID)
   {
-    // Check exclude and include filters first.
-    if (!filtersAllowCaching(entry)) {
-      return;
-    }
-
     // Create the cache entry based on the provided information.
     CacheEntry cacheEntry = new CacheEntry(entry, backend, entryID);
     SoftReference<CacheEntry> ref =
@@ -327,11 +326,6 @@
   public boolean putEntryIfAbsent(Entry entry, Backend backend,
                                   long entryID)
   {
-    // Check exclude and include filters first.
-    if (!filtersAllowCaching(entry)) {
-      return true;
-    }
-
     // See if the entry already exists.  If so, then return false.
     if (dnMap.containsKey(entry.getDN()))
     {
@@ -667,7 +661,9 @@
     try {
       attrs = EntryCacheCommon.getGenericMonitorData(
         new Long(cacheHits.longValue()),
-        new Long(cacheMisses.longValue()),
+        // If cache misses is maintained by default cache
+        // get it from there and if not point to itself.
+        DirectoryServer.getEntryCache().getCacheMisses(),
         null,
         null,
         new Long(dnMap.size()),
@@ -685,6 +681,16 @@
 
 
   /**
+   * {@inheritDoc}
+   */
+  public Long getCacheCount()
+  {
+    return new Long(dnMap.size());
+  }
+
+
+
+  /**
    * Return a verbose string representation of the current cache maps.
    * This is useful primary for debugging and diagnostic purposes such
    * as in the entry cache unit tests.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/monitors/EntryCacheMonitorProvider.java b/opendj-sdk/opends/src/server/org/opends/server/monitors/EntryCacheMonitorProvider.java
index 8cba70c..bdef0f1 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/monitors/EntryCacheMonitorProvider.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/monitors/EntryCacheMonitorProvider.java
@@ -27,21 +27,20 @@
 package org.opends.server.monitors;
 
 import java.util.ArrayList;
+import org.opends.messages.Message;
 
-import org.opends.server.admin.server.ServerManagementContext;
 import org.opends.server.admin.std.server.EntryCacheCfg;
 import org.opends.server.admin.std.server.EntryCacheMonitorProviderCfg;
-import org.opends.server.admin.std.server.RootCfg;
 import org.opends.server.api.EntryCache;
 import org.opends.server.api.MonitorProvider;
+import org.opends.server.config.ConfigConstants;
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.DirectoryServer;
-import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.Attribute;
-import org.opends.server.types.DebugLogLevel;
-import org.opends.server.types.InitializationException;
 
 import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.messages.ConfigMessages.*;
 
 /**
  * This class defines a Directory Server monitor provider that can be used to
@@ -51,29 +50,73 @@
 public class EntryCacheMonitorProvider
        extends MonitorProvider<EntryCacheMonitorProviderCfg>
 {
-  /**
-   * The tracer object for the debug logger.
-   */
-  private static final DebugTracer TRACER = getTracer();
+  // The name for this monitor.
+  private String monitorName;
+
+  // The entry cache common name.
+  private String entryCacheName;
+
+  // The entry cache with which this monitor is associated.
+  private EntryCache<? extends EntryCacheCfg> entryCache;
+
+  // Global entry cache monitor configuration.
+  private static EntryCacheMonitorProviderCfg monitorConfiguration;
 
   /**
-   * Creates an instance of this monitor provider.
+   * Creates default instance of this monitor provider.
    */
   public EntryCacheMonitorProvider()
   {
-    super("Entry Cache Monitor Provider");
+    super("Entry Caches Monitor Provider");
+    this.entryCacheName = "Entry Caches";
+    this.entryCache = (EntryCache<? extends EntryCacheCfg>)
+      DirectoryServer.getEntryCache();
+  }
 
-    // No initialization should be performed here.
+  /**
+   * Creates implementation specific instance of this monitor provider.
+   *
+   * @param  entryCacheName  The name to use for this monitor provider.
+   * @param  entryCache      The entry cache to associate this monitor
+   *                         provider with.
+   */
+  public EntryCacheMonitorProvider(
+    String entryCacheName,
+    EntryCache<? extends EntryCacheCfg> entryCache)
+  {
+    super(entryCacheName + " Entry Cache Monitor Provider");
+    this.entryCacheName = entryCacheName;
+    this.entryCache = entryCache;
   }
 
   /**
    * {@inheritDoc}
    */
   public void initializeMonitorProvider(
-                   EntryCacheMonitorProviderCfg configuration)
-         throws ConfigException, InitializationException
+    EntryCacheMonitorProviderCfg configuration)
+    throws ConfigException
   {
-    // No initialization is required.
+    monitorName = entryCacheName;
+
+    if (configuration != null) {
+      monitorConfiguration = configuration;
+    }
+    if (monitorConfiguration == null) {
+      Message message =
+        INFO_WARN_CONFIG_ENTRYCACHE_NO_MONITOR_CONFIG_ENTRY.get(
+        ConfigConstants.DN_ENTRY_CACHE_MONITOR_CONFIG,
+        monitorName);
+      logError(message);
+      throw new ConfigException(message);
+    }
+    if (!monitorConfiguration.isEnabled()) {
+      Message message =
+        INFO_WARN_CONFIG_ENTRYCACHE_MONITOR_CONFIG_DISABLED.get(
+        ConfigConstants.DN_ENTRY_CACHE_MONITOR_CONFIG,
+        monitorName);
+      logError(message);
+      throw new ConfigException(message);
+    }
   }
 
   /**
@@ -81,7 +124,7 @@
    */
   public String getMonitorInstanceName()
   {
-    return "Entry Cache";
+    return monitorName;
   }
 
   /**
@@ -108,35 +151,12 @@
   public ArrayList<Attribute> getMonitorData()
   {
     ArrayList<Attribute> attrs = new ArrayList<Attribute>();
-    EntryCacheCfg configuration = null;
 
-    // Get the root configuration object.
-    ServerManagementContext managementContext =
-      ServerManagementContext.getInstance();
-    RootCfg rootConfiguration =
-      managementContext.getRootConfiguration();
-
-    // Get the entry cache configuration.
-    try {
-      configuration = rootConfiguration.getEntryCache();
-    } catch (Exception e) {
-      if (debugEnabled()) {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      return attrs;
-    }
-
-    // Get the entry cache.
-    EntryCache<? extends EntryCacheCfg> cache =
-      (EntryCache<? extends EntryCacheCfg>)
-       DirectoryServer.getEntryCache();
-
-    if ((cache != null) &&
-        (configuration != null) &&
-         configuration.isEnabled()) {
-      // Get data from the cache.
-      attrs = cache.getMonitorData();
+    if ((entryCache != null) &&
+        (monitorConfiguration != null) &&
+        (monitorConfiguration.isEnabled())) {
+      // Get monitor data from the cache.
+      attrs = entryCache.getMonitorData();
     }
 
     return attrs;
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DefaultEntryCacheTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DefaultEntryCacheTestCase.java
index ef4e54d..53c5a3e 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DefaultEntryCacheTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DefaultEntryCacheTestCase.java
@@ -22,315 +22,507 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
 
 
+import java.io.File;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
-import java.util.concurrent.locks.Lock;
-
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
+import java.util.SortedMap;
+import java.util.TreeMap;
 import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.server.AdminTestCaseUtils;
+import org.testng.annotations.BeforeClass;
+import org.opends.server.admin.std.meta.*;
+import org.opends.server.admin.std.server.EntryCacheCfg;
+import org.opends.server.admin.std.server.FileSystemEntryCacheCfg;
 import org.opends.server.api.Backend;
+import org.opends.server.api.EntryCache;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
-import org.opends.server.types.LockType;
-
+import org.opends.server.util.ServerConstants;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
 import static org.testng.Assert.*;
 
 
 
 /**
- * A set of test cases for the default entry cache.
+ * A set of test cases for default entry cache implementation.
  */
+@Test(groups = "entrycache", sequential=true)
 public class DefaultEntryCacheTestCase
-       extends ExtensionsTestCase
+       extends CommonEntryCacheTestCase
 {
+  // Entry cache implementations participating in this test.
+  private EntryCache softRefCache = null;
+  private EntryCache fifoCache = null;
+  private EntryCache fsCache = null;
+
+  // ... and their configuration entries.
+  Entry cacheSoftReferenceConfigEntry = null;
+  Entry cacheFIFOConfigEntry = null;
+  Entry cacheFSConfigEntry = null;
+
+  // The entry cache order map sorted by the cache level.
+  private SortedMap<Integer, EntryCache<? extends EntryCacheCfg>>
+    cacheOrderMap = new TreeMap<Integer,
+    EntryCache<? extends EntryCacheCfg>>();
+
+  // Dummy test entries for each participating implementation.
+  private ArrayList<Entry> testSoftRefEntriesList = null;
+  private ArrayList<Entry> testFIFOEntriesList = null;
+  private ArrayList<Entry> testFSEntriesList = null;
+
   /**
-   * Ensures that the Directory Server is running.
+   * Initialize the entry cache test.
    *
    * @throws  Exception  If an unexpected problem occurs.
    */
   @BeforeClass()
-  public void startServer()
+  @SuppressWarnings("unchecked")
+  public void entryCacheTestInit()
          throws Exception
   {
+    // Ensure that the server is running.
     TestCaseUtils.startServer();
+
+    // Get default cache.
+    super.cache = DirectoryServer.getEntryCache();
+
+    // Configure and initialize all entry cache implementations.
+    softRefCache = new SoftReferenceEntryCache();
+    cacheSoftReferenceConfigEntry = TestCaseUtils.makeEntry(
+      "dn: cn=Soft Reference,cn=Entry Caches,cn=config",
+      "objectClass: ds-cfg-soft-reference-entry-cache",
+      "objectClass: ds-cfg-entry-cache",
+      "objectClass: top",
+      "cn: Soft Reference",
+      "ds-cfg-cache-level: 1",
+      "ds-cfg-java-class: " +
+      "org.opends.server.extensions.SoftReferenceEntryCache",
+      "ds-cfg-enabled: true",
+      "ds-cfg-include-filter: uid=softref*",
+      "ds-cfg-include-filter: uid=test1*",
+      "ds-cfg-exclude-filter: uid=test0*");
+    softRefCache.initializeEntryCache(AdminTestCaseUtils.getConfiguration(
+      EntryCacheCfgDefn.getInstance(), cacheSoftReferenceConfigEntry));
+    cacheOrderMap.put(1, softRefCache);
+
+    fifoCache = new FIFOEntryCache();
+    cacheFIFOConfigEntry = TestCaseUtils.makeEntry(
+      "dn: cn=FIFO,cn=Entry Caches,cn=config",
+      "objectClass: ds-cfg-fifo-entry-cache",
+      "objectClass: ds-cfg-entry-cache",
+      "objectClass: top",
+      "cn: FIFO",
+      "ds-cfg-cache-level: 2",
+      "ds-cfg-java-class: org.opends.server.extensions.FIFOEntryCache",
+      "ds-cfg-enabled: true",
+      "ds-cfg-include-filter: uid=fifo*",
+      "ds-cfg-include-filter: uid=test2*",
+      "ds-cfg-exclude-filter: uid=test0*");
+    fifoCache.initializeEntryCache(AdminTestCaseUtils.getConfiguration(
+      EntryCacheCfgDefn.getInstance(), cacheFIFOConfigEntry));
+    cacheOrderMap.put(2, fifoCache);
+
+    fsCache = new FileSystemEntryCache();
+    cacheFSConfigEntry = TestCaseUtils.makeEntry(
+      "dn: cn=File System,cn=Entry Caches,cn=config",
+      "objectClass: ds-cfg-file-system-entry-cache",
+      "objectClass: ds-cfg-entry-cache",
+      "objectClass: top",
+      "cn: File System",
+      "ds-cfg-cache-level: 3",
+      "ds-cfg-java-class: " +
+      "org.opends.server.extensions.FileSystemEntryCache",
+      "ds-cfg-enabled: true",
+      "ds-cfg-include-filter: uid=fs*",
+      "ds-cfg-include-filter: uid=test3*",
+      "ds-cfg-include-filter: uid=test0*");
+    fsCache.initializeEntryCache(AdminTestCaseUtils.getConfiguration(
+      EntryCacheCfgDefn.getInstance(), cacheFSConfigEntry));
+    cacheOrderMap.put(3, fsCache);
+
+    // Plug all cache implementations into default entry cache.
+    final Method[] defaultCacheMethods =
+        super.cache.getClass().getDeclaredMethods();
+    for (int i = 0; i < defaultCacheMethods.length; ++i) {
+      if (defaultCacheMethods[i].getName().equals("setCacheOrder")) {
+        defaultCacheMethods[i].setAccessible(true);
+        Object arglist[] = new Object[] { cacheOrderMap };
+        defaultCacheMethods[i].invoke(cache, arglist);
+      }
+    }
+
+    // Make some dummy test entries.
+    super.testEntriesList = new ArrayList<Entry>(super.NUMTESTENTRIES);
+    for(int i = 0; i < super.NUMTESTENTRIES; i++ ) {
+      super.testEntriesList.add(TestCaseUtils.makeEntry(
+        "dn: uid=test" + Integer.toString(i) + ".user" + Integer.toString(i)
+         + ",ou=test" + Integer.toString(i) + ",o=test",
+        "objectClass: person",
+        "objectClass: inetorgperson",
+        "objectClass: top",
+        "objectClass: organizationalperson",
+        "postalAddress: somewhere in Testville" + Integer.toString(i),
+        "street: Under Construction Street" + Integer.toString(i),
+        "l: Testcounty" + Integer.toString(i),
+        "st: Teststate" + Integer.toString(i),
+        "telephoneNumber: +878 8378 8378" + Integer.toString(i),
+        "mobile: +878 8378 8378" + Integer.toString(i),
+        "homePhone: +878 8378 8378" + Integer.toString(i),
+        "pager: +878 8378 8378" + Integer.toString(i),
+        "mail: test" + Integer.toString(i) + ".user" + Integer.toString(i)
+         + "@testdomain.net",
+        "postalCode: 8378" + Integer.toString(i),
+        "userPassword: testpassword" + Integer.toString(i),
+        "description: description for Test" + Integer.toString(i) + "User"
+         + Integer.toString(i),
+        "cn: Test" + Integer.toString(i) + "User" + Integer.toString(i),
+        "sn: User" + Integer.toString(i),
+        "givenName: Test" + Integer.toString(i),
+        "initials: TST" + Integer.toString(i),
+        "employeeNumber: 8378" + Integer.toString(i),
+        "uid: test" + Integer.toString(i) + ".user" + Integer.toString(i))
+      );
+    }
+    testSoftRefEntriesList = new ArrayList<Entry>(super.NUMTESTENTRIES);
+    for(int i = 0; i < super.NUMTESTENTRIES; i++ ) {
+      testSoftRefEntriesList.add(TestCaseUtils.makeEntry(
+        "dn: uid=softref" + Integer.toString(i) + ".user" + Integer.toString(i)
+         + ",ou=test" + Integer.toString(i) + ",o=test",
+        "objectClass: person",
+        "objectClass: inetorgperson",
+        "objectClass: top",
+        "objectClass: organizationalperson",
+        "uid: softref" + Integer.toString(i) + ".user" + Integer.toString(i))
+      );
+    }
+    testFIFOEntriesList = new ArrayList<Entry>(super.NUMTESTENTRIES);
+    for(int i = 0; i < super.NUMTESTENTRIES; i++ ) {
+      testFIFOEntriesList.add(TestCaseUtils.makeEntry(
+        "dn: uid=fifo" + Integer.toString(i) + ".user" + Integer.toString(i)
+         + ",ou=test" + Integer.toString(i) + ",o=test",
+        "objectClass: person",
+        "objectClass: inetorgperson",
+        "objectClass: top",
+        "objectClass: organizationalperson",
+        "uid: fifo" + Integer.toString(i) + ".user" + Integer.toString(i))
+      );
+    }
+    testFSEntriesList = new ArrayList<Entry>(super.NUMTESTENTRIES);
+    for(int i = 0; i < super.NUMTESTENTRIES; i++ ) {
+      testFSEntriesList.add(TestCaseUtils.makeEntry(
+        "dn: uid=fs" + Integer.toString(i) + ".user" + Integer.toString(i)
+         + ",ou=test" + Integer.toString(i) + ",o=test",
+        "objectClass: person",
+        "objectClass: inetorgperson",
+        "objectClass: top",
+        "objectClass: organizationalperson",
+        "uid: fs" + Integer.toString(i) + ".user" + Integer.toString(i))
+      );
+    }
+
+    // Force GC to make sure we have enough memory for
+    // the cache capping constraints to work properly.
+    System.gc();
   }
 
 
 
   /**
-   * Tests the process of creating, initializing, and finalizing the cache.
+   * Finalize the entry cache test.
    *
    * @throws  Exception  If an unexpected problem occurs.
    */
-  @Test()
-  public void testInitializeAndFinalizeCache()
+  @AfterClass()
+  public void entryCacheTestFini()
          throws Exception
   {
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-    cache.finalizeEntryCache();
+    // Finilize all entry cache implementations.
+    for (EntryCache entryCache : cacheOrderMap.values()) {
+      entryCache.finalizeEntryCache();
+    }
+
+    // Remove default FS cache JE environment.
+    FileSystemEntryCacheCfg config = (FileSystemEntryCacheCfg)
+      AdminTestCaseUtils.getConfiguration(EntryCacheCfgDefn.getInstance(),
+      cacheFSConfigEntry);
+    TestCaseUtils.deleteDirectory(new File(config.getCacheDirectory()));
   }
 
 
 
   /**
-   * Tests the <CODE>containsEntry</CODE> method.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
   @Test()
+  @Override
   public void testContainsEntry()
          throws Exception
   {
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-
-    assertFalse(cache.containsEntry(DN.decode("uid=test,o=test")));
-
-    cache.finalizeEntryCache();
+    super.testContainsEntry();
   }
 
 
 
   /**
-   * Tests the first <CODE>getEntry</CODE> method, which takes a single DN
-   * argument.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
   @Test()
+  @Override
   public void testGetEntry1()
          throws Exception
   {
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-
-    assertNull(cache.getEntry(DN.decode("uid=test,o=test")));
-
-    cache.finalizeEntryCache();
+    super.testGetEntry1();
   }
 
 
 
   /**
-   * Tests the second <CODE>getEntry</CODE> method, which takes a DN, lock type,
-   * and list attributes.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
   @Test()
+  @Override
   public void testGetEntry2()
          throws Exception
   {
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-
-    assertNull(cache.getEntry(DN.decode("uid=test,o=test"), LockType.NONE,
-                              new ArrayList<Lock>()));
-
-    cache.finalizeEntryCache();
+    super.testGetEntry2();
   }
 
 
 
   /**
-   * Tests the third <CODE>getEntry</CODE> method, which takes a backend, entry
-   * ID, lock type, and list attributes.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
   @Test()
+  @Override
   public void testGetEntry3()
          throws Exception
   {
-    TestCaseUtils.initializeTestBackend(false);
-    Backend b = DirectoryServer.getBackend(DN.decode("o=test"));
-
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-
-    assertNull(cache.getEntry(b, -1, LockType.NONE, new ArrayList<Lock>()));
-
-    cache.finalizeEntryCache();
+    super.testGetEntry3();
   }
 
 
 
   /**
-   * Tests the <CODE>getEntryID</CODE> method.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
   @Test()
+  @Override
   public void testGetEntryID()
          throws Exception
   {
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-
-    assertEquals(cache.getEntryID(DN.decode("uid=test,o=test")), -1);
-
-    cache.finalizeEntryCache();
+    super.testGetEntryID();
   }
 
 
 
   /**
-   * Tests the <CODE>putEntry</CODE> method.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
   @Test()
+  @Override
   public void testPutEntry()
          throws Exception
   {
-    TestCaseUtils.initializeTestBackend(false);
-    Backend b = DirectoryServer.getBackend(DN.decode("o=test"));
-
-    Entry e = TestCaseUtils.makeEntry("dn: o=test",
-                                      "objectClass: top",
-                                      "objectClass: organization",
-                                      "o: test");
-
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-
-    cache.putEntry(e, b, 1);
-
-    cache.finalizeEntryCache();
+    super.testPutEntry();
   }
 
 
 
   /**
-   * Tests the <CODE>putEntryIfAbsent</CODE> method.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
   @Test()
+  @Override
   public void testPutEntryIfAbsent()
          throws Exception
   {
-    TestCaseUtils.initializeTestBackend(false);
-    Backend b = DirectoryServer.getBackend(DN.decode("o=test"));
-
-    Entry e = TestCaseUtils.makeEntry("dn: o=test",
-                                      "objectClass: top",
-                                      "objectClass: organization",
-                                      "o: test");
-
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-
-    assertTrue(cache.putEntryIfAbsent(e, b, 1));
-
-    cache.finalizeEntryCache();
+    super.testPutEntryIfAbsent();
   }
 
 
 
   /**
-   * Tests the <CODE>removeEntry</CODE> method.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
   @Test()
+  @Override
   public void testRemoveEntry()
          throws Exception
   {
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-
-    cache.removeEntry(DN.decode("uid=test,o=test"));
-
-    cache.finalizeEntryCache();
+    super.testRemoveEntry();
   }
 
 
 
   /**
-   * Tests the <CODE>clear</CODE> method.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
   @Test()
+  @Override
   public void testClear()
          throws Exception
   {
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-
-    cache.clear();
-
-    cache.finalizeEntryCache();
+    super.testClear();
   }
 
 
 
   /**
-   * Tests the <CODE>clearBackend</CODE> method.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
   @Test()
+  @Override
   public void testClearBackend()
          throws Exception
   {
-    TestCaseUtils.initializeTestBackend(false);
-    Backend b = DirectoryServer.getBackend(DN.decode("o=test"));
-
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-
-    cache.clearBackend(b);
-
-    cache.finalizeEntryCache();
+    super.testClearBackend();
   }
 
 
 
   /**
-   * Tests the <CODE>clearSubtree</CODE> method.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
   @Test()
+  @Override
   public void testClearSubtree()
          throws Exception
   {
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
-
-    cache.clearSubtree(DN.decode("o=test"));
-
-    cache.finalizeEntryCache();
+    super.testClearSubtree();
   }
 
 
 
   /**
-   * Tests the <CODE>handleLowMemory</CODE> method.
+   * {@inheritDoc}
+   */
+  @Test()
+  @Override
+  public void testHandleLowMemory()
+         throws Exception
+  {
+    super.testHandleLowMemory();
+  }
+
+
+
+  /**
+   * Tests the entry cache level functionality where each set
+   * of entries land on a specific cache level by some form
+   * of selection criteria such as include / exclude filters.
    *
    * @throws  Exception  If an unexpected problem occurs.
    */
   @Test()
-  public void handleLowMemory()
+  public void testCacheLevels()
          throws Exception
   {
-    DefaultEntryCache cache = new DefaultEntryCache();
-    cache.initializeEntryCache(null);
+    assertNull(toVerboseString(),
+      "Expected empty cache.  " + "Cache contents:" + ServerConstants.EOL +
+      toVerboseString());
 
-    cache.handleLowMemory();
+    TestCaseUtils.initializeTestBackend(false);
+    Backend b = DirectoryServer.getBackend(DN.decode("o=test"));
 
-    cache.finalizeEntryCache();
+    // Spread test entries among all cache levels via default cache.
+    for (int i = 0; i < NUMTESTENTRIES; i++) {
+      super.cache.putEntry(testSoftRefEntriesList.get(i), b, i);
+      super.cache.putEntry(testFIFOEntriesList.get(i), b, i);
+      super.cache.putEntry(testFSEntriesList.get(i), b, i);
+    }
+
+    // Ensure all test entries are available via default cache.
+    for (int i = 0; i < NUMTESTENTRIES; i++) {
+      assertNotNull(super.cache.getEntry(
+        testSoftRefEntriesList.get(0).getDN()),
+        "Expected to find " +
+        testSoftRefEntriesList.get(0).getDN().toString() +
+        " in the cache.  Cache contents:" +
+        ServerConstants.EOL + toVerboseString());
+      assertNotNull(super.cache.getEntry(
+        testFIFOEntriesList.get(0).getDN()),
+        "Expected to find " +
+        testFIFOEntriesList.get(0).getDN().toString() +
+        " in the cache.  Cache contents:" +
+        ServerConstants.EOL + toVerboseString());
+      assertNotNull(super.cache.getEntry(
+        testFSEntriesList.get(0).getDN()),
+        "Expected to find " +
+        testFSEntriesList.get(0).getDN().toString() +
+        " in the cache.  Cache contents:" +
+        ServerConstants.EOL + toVerboseString());
+    }
+
+    // Ensure all test entries landed on their levels.
+    for (int i = 0; i < NUMTESTENTRIES; i++) {
+      assertNotNull(softRefCache.getEntry(
+        testSoftRefEntriesList.get(0).getDN()),
+        "Expected to find " +
+        testSoftRefEntriesList.get(0).getDN().toString() +
+        " in the cache.  Cache contents:" +
+        ServerConstants.EOL + toVerboseString());
+      assertNotNull(fifoCache.getEntry(
+        testFIFOEntriesList.get(0).getDN()),
+        "Expected to find " +
+        testFIFOEntriesList.get(0).getDN().toString() +
+        " in the cache.  Cache contents:" +
+        ServerConstants.EOL + toVerboseString());
+      assertNotNull(fsCache.getEntry(
+        testFSEntriesList.get(0).getDN()),
+        "Expected to find " +
+        testFSEntriesList.get(0).getDN().toString() +
+        " in the cache.  Cache contents:" +
+        ServerConstants.EOL + toVerboseString());
+    }
+
+    // Clear the cache so that other tests can start from scratch.
+    super.cache.clear();
+  }
+
+
+
+  @BeforeGroups(groups = "testDefaultCacheConcurrency")
+  public void cacheConcurrencySetup()
+         throws Exception
+  {
+    assertNull(super.toVerboseString(),
+      "Expected empty cache.  " + "Cache contents:" + ServerConstants.EOL +
+      super.toVerboseString());
+  }
+
+
+
+  @AfterGroups(groups = "testDefaultCacheConcurrency")
+  public void cacheConcurrencyCleanup()
+         throws Exception
+  {
+    // Clear the cache so that other tests can start from scratch.
+    super.cache.clear();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Test(groups = { "slow", "testDefaultCacheConcurrency" },
+        threadPoolSize = 10,
+        invocationCount = 10,
+        timeOut = 60000)
+  @Override
+  public void testCacheConcurrency()
+         throws Exception
+  {
+    super.testCacheConcurrency();
   }
 }
-
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FIFOEntryCacheTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FIFOEntryCacheTestCase.java
index 408a864..9813778 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FIFOEntryCacheTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FIFOEntryCacheTestCase.java
@@ -68,11 +68,12 @@
 
     // Configure this entry cache.
     Entry cacheConfigEntry = TestCaseUtils.makeEntry(
-      "dn: cn=Entry Cache,cn=config",
+      "dn: cn=FIFO,cn=Entry Caches,cn=config",
       "objectClass: ds-cfg-fifo-entry-cache",
       "objectClass: ds-cfg-entry-cache",
       "objectClass: top",
-      "cn: Entry Cache",
+      "cn: FIFO",
+      "ds-cfg-cache-level: 1",
       "ds-cfg-java-class: org.opends.server.extensions.FIFOEntryCache",
       "ds-cfg-enabled: true",
       "ds-cfg-max-entries: " + Integer.toString(super.MAXENTRIES));
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FileSystemEntryCacheTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FileSystemEntryCacheTestCase.java
index 22ebcc6..cf80800 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FileSystemEntryCacheTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FileSystemEntryCacheTestCase.java
@@ -40,9 +40,7 @@
 import org.opends.server.types.Attribute;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
-import org.opends.server.types.OperatingSystem;
 import org.opends.server.util.ServerConstants;
-import org.opends.server.util.StaticUtils;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.AfterGroups;
 import org.testng.annotations.BeforeGroups;
@@ -200,11 +198,12 @@
 
     // Configure this entry cache.
     cacheConfigEntry = TestCaseUtils.makeEntry(
-      "dn: cn=Entry Cache,cn=config",
+      "dn: cn=File System,cn=Entry Caches,cn=config",
       "objectClass: ds-cfg-file-system-entry-cache",
       "objectClass: ds-cfg-entry-cache",
       "objectClass: top",
-      "cn: Entry Cache",
+      "cn: File System",
+      "ds-cfg-cache-level: 1",
       "ds-cfg-java-class: " +
       "org.opends.server.extensions.FileSystemEntryCache",
       "ds-cfg-enabled: true",
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SoftReferenceEntryCacheTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SoftReferenceEntryCacheTestCase.java
index 612d0be..74e4ba9 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SoftReferenceEntryCacheTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SoftReferenceEntryCacheTestCase.java
@@ -65,11 +65,12 @@
 
     // Configure this entry cache.
     Entry cacheConfigEntry = TestCaseUtils.makeEntry(
-      "dn: cn=Entry Cache,cn=config",
+      "dn: cn=Soft Reference,cn=Entry Caches,cn=config",
       "objectClass: ds-cfg-soft-reference-entry-cache",
       "objectClass: ds-cfg-entry-cache",
       "objectClass: top",
-      "cn: Entry Cache",
+      "cn: Soft Reference",
+      "ds-cfg-cache-level: 1",
       "ds-cfg-java-class: " +
       "org.opends.server.extensions.SoftReferenceEntryCache",
       "ds-cfg-enabled: true");

--
Gitblit v1.10.0