From 100e551066fc2b42a7578906a0a7a734896a2961 Mon Sep 17 00:00:00 2001
From: abobrov <abobrov@localhost>
Date: Tue, 08 Jan 2008 10:13:19 +0000
Subject: [PATCH] - [Issue 1595] need a fast way to prime the FileSystemEntryCache:   Add generic entry cache pre-load mechanism which is backend and entry cache implementations independent.

---
 opends/resource/schema/02-config.ldif                                                                 |   13 
 opends/src/server/org/opends/server/core/EntryCacheConfigManager.java                                 |   10 
 opends/src/admin/defn/org/opends/server/admin/std/DefaultEntryCacheConfiguration.xml                  |   69 ++++
 opends/src/server/org/opends/server/backends/BackupBackend.java                                       |   13 
 opends/src/server/org/opends/server/api/Backend.java                                                  |   23 +
 opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml                               |   13 
 opends/resource/config/config.ldif                                                                    |    3 
 opends/src/server/org/opends/server/backends/LDIFBackend.java                                         |   20 +
 opends/src/server/org/opends/server/backends/MemoryBackend.java                                       |   15 
 opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/PreloadEntryCacheTestCase.java |  235 +++++++++++++
 opends/src/server/org/opends/server/backends/MonitorBackend.java                                      |   14 
 opends/src/server/org/opends/server/extensions/EntryCachePreloader.java                               |  386 ++++++++++++++++++++++
 opends/src/server/org/opends/server/backends/jeb/BackendImpl.java                                     |   50 ++
 opends/src/server/org/opends/server/backends/RootDSEBackend.java                                      |   13 
 opends/src/server/org/opends/server/backends/TrustStoreBackend.java                                   |   14 
 opends/src/server/org/opends/server/extensions/ConfigFileHandler.java                                 |   14 
 opends/src/server/org/opends/server/replication/server/ReplicationBackend.java                        |   14 
 opends/src/messages/messages/extension.properties                                                     |   19 +
 opends/src/server/org/opends/server/backends/SchemaBackend.java                                       |   14 
 opends/src/server/org/opends/server/backends/task/TaskBackend.java                                    |   14 
 20 files changed, 948 insertions(+), 18 deletions(-)

diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 308bff9..8e6d921 100644
--- a/opends/resource/config/config.ldif
+++ b/opends/resource/config/config.ldif
@@ -20,7 +20,7 @@
 #
 # CDDL HEADER END
 #
-#      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+#      Portions Copyright 2006-2008 Sun Microsystems, Inc.
 #
 #
 # This file contains the primary Directory Server configuration.  It must not
@@ -490,6 +490,7 @@
 dn: cn=Entry Caches,cn=config
 objectClass: top
 objectClass: ds-cfg-branch
+objectClass: ds-cfg-default-entry-cache
 cn: Entry Caches
 
 dn: cn=FIFO,cn=Entry Caches,cn=config
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index 3b6cb05..a88fbdf 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -21,7 +21,7 @@
 # CDDL HEADER END
 #
 #
-#      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+#      Portions Copyright 2006-2008 Sun Microsystems, Inc.
 #
 #
 # This file contains the attribute type and objectclass definitions for use
@@ -2177,6 +2177,11 @@
   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.446
+  NAME 'ds-cfg-cache-preload'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler'
   SUP top
@@ -3677,3 +3682,9 @@
   STRUCTURAL
   MUST ( ds-cfg-backend )
   X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.180
+  NAME 'ds-cfg-default-entry-cache'
+  SUP ds-cfg-branch
+  STRUCTURAL
+  MAY ds-cfg-cache-preload
+  X-ORIGIN 'OpenDS Directory Server' )
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/DefaultEntryCacheConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/DefaultEntryCacheConfiguration.xml
new file mode 100644
index 0000000..24d568d
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/DefaultEntryCacheConfiguration.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ! CDDL HEADER START
+  !
+  ! The contents of this file are subject to the terms of the
+  ! Common Development and Distribution License, Version 1.0 only
+  ! (the "License").  You may not use this file except in compliance
+  ! with the License.
+  !
+  ! You can obtain a copy of the license at
+  ! trunk/opends/resource/legal-notices/OpenDS.LICENSE
+  ! or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+  ! See the License for the specific language governing permissions
+  ! and limitations under the License.
+  !
+  ! When distributing Covered Code, include this CDDL HEADER in each
+  ! file and include the License file at
+  ! trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+  ! add the following below this CDDL HEADER, with the fields enclosed
+  ! by brackets "[]" replaced with your own identifying information:
+  !      Portions Copyright [yyyy] [name of copyright owner]
+  !
+  ! CDDL HEADER END
+  !
+  !
+  !      Portions Copyright 2008 Sun Microsystems, Inc.
+  ! -->
+<adm:managed-object name="default-entry-cache" plural-name="default-entry-cache"
+  package="org.opends.server.admin.std"
+  xmlns:adm="http://www.opends.org/admin"
+  xmlns:ldap="http://www.opends.org/admin-ldap"
+  xmlns:cli="http://www.opends.org/admin-cli">
+  <adm:synopsis>
+    <adm:user-friendly-name />
+    represents the Directory Server entry cache framework.
+  </adm:synopsis>
+  <adm:tag name="database" />
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:name>ds-cfg-default-entry-cache</ldap:name>
+      <ldap:superior>ds-cfg-branch</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+  <adm:profile name="cli">
+    <cli:managed-object custom="true" />
+  </adm:profile>
+  <adm:property name="cache-preload" mandatory="false">
+    <adm:synopsis>
+      Indicates whether or not to preload the entry cache on startup.
+    </adm:synopsis>
+    <adm:requires-admin-action>
+      <adm:server-restart />
+    </adm:requires-admin-action>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>false</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:boolean />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-cache-preload</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+</adm:managed-object>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
index a61c45e..67334c8 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
@@ -23,7 +23,7 @@
   ! CDDL HEADER END
   !
   !
-  !      Portions Copyright 2007 Sun Microsystems, Inc.
+  !      Portions Copyright 2007-2008 Sun Microsystems, Inc.
   ! -->
 <adm:root-managed-object xmlns:adm="http://www.opends.org/admin"
   xmlns:ldap="http://www.opends.org/admin-ldap"
@@ -203,6 +203,17 @@
       </cli:relation>
     </adm:profile>
   </adm:relation>
+  <adm:relation name="default-entry-cache">
+    <adm:one-to-one />
+    <adm:profile name="ldap">
+      <ldap:rdn-sequence>cn=Entry Caches,cn=config</ldap:rdn-sequence>
+    </adm:profile>
+    <adm:profile name="cli">
+      <cli:relation>
+        <cli:default-property name="cache-preload" />
+      </cli:relation>
+    </adm:profile>
+  </adm:relation>
   <adm:relation name="entry-cache">
     <adm:one-to-many />
     <adm:profile name="ldap">
diff --git a/opends/src/messages/messages/extension.properties b/opends/src/messages/messages/extension.properties
index f4100b1..12eaff6 100644
--- a/opends/src/messages/messages/extension.properties
+++ b/opends/src/messages/messages/extension.properties
@@ -20,7 +20,7 @@
 #
 # CDDL HEADER END
 #
-#      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+#      Portions Copyright 2006-2008 Sun Microsystems, Inc.
 
 
 
@@ -72,6 +72,21 @@
  to initialize fifo entry cache:  %s
 FATAL_ERR_SOFTREFCACHE_CANNOT_INITIALIZE_9=A fatal error occurred while \
  trying to initialize soft reference entry cache:  %s
+NOTICE_CACHE_PRELOAD_PROGRESS_START_10=Starting the entry cache preload
+NOTICE_CACHE_PRELOAD_PROGRESS_REPORT_11=The entry cache preload has processed \
+ %d entries, %d MB free heap memory available
+NOTICE_CACHE_PRELOAD_PROGRESS_DONE_12=The entry cache preload is complete \
+ with the total of %d entries processed
+SEVERE_WARN_CACHE_PRELOAD_INTERRUPTED_13=The entry cache preload has been \
+ interrupted
+SEVERE_ERR_CACHE_PRELOAD_COLLECTOR_FAILED_14=The entry cache preload was \
+ unable to complete collector processing for %s backend, and as a result \
+ the entry cache preload for this backend will be incomplete
+SEVERE_WARN_CACHE_PRELOAD_BACKEND_FAILED_15=The entry cache preload is not \
+ supported by %s backend, and as a result no entries from this backend will \
+ be preloaded onto the entry cache
+SEVERE_ERR_CACHE_PRELOAD_ENTRY_FAILED_16=Failed to preload %s entry onto \
+ the entry cache:  %s
 MILD_ERR_EXTOP_PASSMOD_ILLEGAL_REQUEST_ELEMENT_TYPE_32=The password modify \
  extended request sequence included an ASN.1 element of an invalid type:  %s
 MILD_ERR_EXTOP_PASSMOD_CANNOT_DECODE_REQUEST_33=An unexpected error occurred \
@@ -1613,4 +1628,4 @@
 SEVERE_ERR_SDTUACM_ATTR_UNINDEXED_569=The subject DN to user attribute \
  certificate mapper defined in configuration entry %s references attribute \
  type %s which is does not have an equality index defined in backend %s
- 
+
diff --git a/opends/src/server/org/opends/server/api/Backend.java b/opends/src/server/org/opends/server/api/Backend.java
index fc26580..3775e7a 100644
--- a/opends/src/server/org/opends/server/api/Backend.java
+++ b/opends/src/server/org/opends/server/api/Backend.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.api;
 import org.opends.messages.Message;
@@ -30,6 +30,7 @@
 
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
@@ -210,6 +211,26 @@
 
 
   /**
+   * Retrieves the set of all DNs that are stored within this backend.
+   * Note that this can be a slow operation depending on a particular
+   * backend implementation and might be unsupported by some backends.
+   *
+   * @param   storedDNs  Collection to retrieve all stored DNs into.
+   *          Note that for async operation a thread-safe collection
+   *          should be used.
+   *
+   * @return  {@code true} if all DNs stored within this backend were
+   *          successfully retrieved, or {@code false} otherwise.
+   *
+   * @throws  UnsupportedOperationException if backend implementation
+   *          does not support this operation.
+   */
+  public abstract boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException;
+
+
+
+  /**
    * Indicates whether the data associated with this backend may be
    * considered local (i.e., in a repository managed by the Directory
    * Server) rather than remote (i.e., in an external repository
diff --git a/opends/src/server/org/opends/server/backends/BackupBackend.java b/opends/src/server/org/opends/server/backends/BackupBackend.java
index bc024fd..68f1dd0 100644
--- a/opends/src/server/org/opends/server/backends/BackupBackend.java
+++ b/opends/src/server/org/opends/server/backends/BackupBackend.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.backends;
 
@@ -1297,5 +1297,16 @@
          new AttributeValue(rdnAttrType, rdnStringValue);
     return parentDN.concat(RDN.create(rdnAttrType, attrValue));
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException
+  {
+    throw new UnsupportedOperationException("Operation not supported.");
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/backends/LDIFBackend.java b/opends/src/server/org/opends/server/backends/LDIFBackend.java
index e1251b4..1e06268 100644
--- a/opends/src/server/org/opends/server/backends/LDIFBackend.java
+++ b/opends/src/server/org/opends/server/backends/LDIFBackend.java
@@ -22,13 +22,14 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2007-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.backends;
 
 
 
 import java.io.File;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -1572,5 +1573,22 @@
 
     return alerts;
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException
+  {
+    backendLock.readLock().lock();
+    try {
+      storedDNs.addAll(entryMap.keySet());
+      return true;
+    } finally {
+      backendLock.readLock().unlock();
+    }
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/backends/MemoryBackend.java b/opends/src/server/org/opends/server/backends/MemoryBackend.java
index 9ec6f22..e3524e8 100644
--- a/opends/src/server/org/opends/server/backends/MemoryBackend.java
+++ b/opends/src/server/org/opends/server/backends/MemoryBackend.java
@@ -22,12 +22,13 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.backends;
 
 
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -987,5 +988,17 @@
     Message message = ERR_MEMORYBACKEND_BACKUP_RESTORE_NOT_SUPPORTED.get();
     throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public synchronized boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException
+  {
+    storedDNs.addAll(entryMap.keySet());
+    return true;
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/backends/MonitorBackend.java b/opends/src/server/org/opends/server/backends/MonitorBackend.java
index 504e064..1b739d6 100644
--- a/opends/src/server/org/opends/server/backends/MonitorBackend.java
+++ b/opends/src/server/org/opends/server/backends/MonitorBackend.java
@@ -22,13 +22,14 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.backends;
 
 
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -1276,5 +1277,16 @@
     currentConfig = backendCfg;
     return new ConfigChangeResult(resultCode, adminActionRequired, messages);
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException
+  {
+    throw new UnsupportedOperationException("Operation not supported.");
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/backends/RootDSEBackend.java b/opends/src/server/org/opends/server/backends/RootDSEBackend.java
index 959d022..f68a60b 100644
--- a/opends/src/server/org/opends/server/backends/RootDSEBackend.java
+++ b/opends/src/server/org/opends/server/backends/RootDSEBackend.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.backends;
 
@@ -1529,5 +1529,16 @@
 
     return new ConfigChangeResult(resultCode, adminActionRequired, messages);
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException
+  {
+    throw new UnsupportedOperationException("Operation not supported.");
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/backends/SchemaBackend.java b/opends/src/server/org/opends/server/backends/SchemaBackend.java
index de9f810..e70a6e4 100644
--- a/opends/src/server/org/opends/server/backends/SchemaBackend.java
+++ b/opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.backends;
 
@@ -37,6 +37,7 @@
 import java.security.MessageDigest;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -5575,5 +5576,16 @@
     return
           new Attribute(attributeTypesType, ATTR_ATTRIBUTE_TYPES, valueSetCopy);
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException
+  {
+    throw new UnsupportedOperationException("Operation not supported.");
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/backends/TrustStoreBackend.java b/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
index 678dd60..d4db669 100644
--- a/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
+++ b/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2007-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.backends;
 
@@ -42,6 +42,7 @@
 import java.security.KeyStoreException;
 import java.security.cert.Certificate;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
@@ -1898,5 +1899,16 @@
     }
 
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException
+  {
+    throw new UnsupportedOperationException("Operation not supported.");
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java b/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
index 7a16bdf..bbdb7a8 100644
--- a/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
+++ b/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
@@ -22,9 +22,12 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2007-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.backends.jeb;
+import com.sleepycat.je.Cursor;
+import com.sleepycat.je.CursorConfig;
+import com.sleepycat.je.DatabaseEntry;
 import org.opends.messages.Message;
 
 import java.io.IOException;
@@ -39,6 +42,8 @@
 
 import com.sleepycat.je.DatabaseException;
 import com.sleepycat.je.EnvironmentConfig;
+import com.sleepycat.je.LockMode;
+import com.sleepycat.je.OperationStatus;
 import com.sleepycat.je.RunRecoveryException;
 
 import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn;
@@ -67,6 +72,8 @@
 import org.opends.server.admin.std.server.LocalDBBackendCfg;
 import org.opends.server.admin.Configuration;
 import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.types.DN;
 
 /**
  * This is an implementation of a Directory Server Backend which stores entries
@@ -1729,4 +1736,45 @@
       throw new InitializationException(message, e);
     }
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException
+  {
+    for (EntryContainer entryContainer : rootContainer.getEntryContainers()) {
+      DN2ID dn2id = entryContainer.getDN2ID();
+      Cursor cursor = null;
+      try {
+        cursor = dn2id.openCursor(null, new CursorConfig());
+        DatabaseEntry key = new DatabaseEntry();
+        DatabaseEntry data = new DatabaseEntry();
+        OperationStatus status;
+        for (status = cursor.getFirst(key, data, LockMode.DEFAULT);
+             status == OperationStatus.SUCCESS;
+             status = cursor.getNext(key, data, LockMode.DEFAULT)) {
+          DN entryDN = DN.decode(new ASN1OctetString(key.getData()));
+          storedDNs.add(entryDN);
+        }
+      } catch (Exception e) {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+        return false;
+      } finally {
+        if (cursor != null) {
+          try {
+            cursor.close();
+          } catch (DatabaseException de) {
+            if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, de);
+            }
+          }
+        }
+      }
+    }
+    return true;
+  }
 }
diff --git a/opends/src/server/org/opends/server/backends/task/TaskBackend.java b/opends/src/server/org/opends/server/backends/task/TaskBackend.java
index e81ff8a..631be58 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskBackend.java
+++ b/opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.backends.task;
 
@@ -31,6 +31,7 @@
 import java.io.File;
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -1544,5 +1545,16 @@
   {
     return taskScheduler.getRecurringTask(taskEntryDN);
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException
+  {
+    throw new UnsupportedOperationException("Operation not supported.");
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/core/EntryCacheConfigManager.java b/opends/src/server/org/opends/server/core/EntryCacheConfigManager.java
index c803931..21ce273 100644
--- a/opends/src/server/org/opends/server/core/EntryCacheConfigManager.java
+++ b/opends/src/server/org/opends/server/core/EntryCacheConfigManager.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.core;
 import org.opends.messages.Message;
@@ -57,6 +57,7 @@
 import org.opends.server.config.ConfigConstants;
 import org.opends.server.config.ConfigEntry;
 import org.opends.server.extensions.DefaultEntryCache;
+import org.opends.server.extensions.EntryCachePreloader;
 import org.opends.server.monitors.EntryCacheMonitorProvider;
 import org.opends.server.types.DN;
 
@@ -225,6 +226,13 @@
         }
       }
     }
+
+    // If requested preload the entry cache.
+    if (rootConfiguration.getDefaultEntryCache().isCachePreload()) {
+      // Kick off preload arbiter main thread.
+      EntryCachePreloader preloadThread = new EntryCachePreloader();
+      preloadThread.start();
+    }
   }
 
 
diff --git a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
index 6b57243..90e2ff5 100644
--- a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
+++ b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
@@ -36,6 +36,7 @@
 import java.io.OutputStream;
 import java.security.MessageDigest;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -3615,5 +3616,16 @@
       logError(message);
     }
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException
+  {
+    throw new UnsupportedOperationException("Operation not supported.");
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/extensions/EntryCachePreloader.java b/opends/src/server/org/opends/server/extensions/EntryCachePreloader.java
new file mode 100644
index 0000000..847952e
--- /dev/null
+++ b/opends/src/server/org/opends/server/extensions/EntryCachePreloader.java
@@ -0,0 +1,386 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.extensions;
+import org.opends.messages.Message;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Lock;
+import org.opends.server.api.Backend;
+import org.opends.server.api.DirectoryThread;
+import org.opends.server.api.ServerShutdownListener;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.loggers.debug.DebugTracer;
+
+import org.opends.server.types.LockManager;
+import static org.opends.server.util.StaticUtils.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.loggers.ErrorLogger.logError;
+import static org.opends.messages.ExtensionMessages.*;
+
+/**
+ * This class defines a utility that will be used to pre-load the Directory
+ * Server entry cache.  Pre-loader is multi-threaded and consist of the
+ * following threads:
+ *
+ * - The Arbiter thread which monitors overall pre-load progress and manages
+ *   pre-load worker threads by adding or removing them as deemed necessary.
+ *
+ * - The Collector thread which collects all entry DNs stored within every
+ *   configured and active backend to a shared object workers consume from.
+ *
+ * - Worker threads which are responsible for monitoring the collector feed
+ *   and requesting the actual entries for retrieval and in cache storage.
+ *
+ * This implementation is entry cache and backend independent and can be
+ * used to pre-load from any backend to any entry cache as long as both
+ * are capable of initiating and sustaining such pre-load activity.
+ *
+ * This implementation is fully synchronized and safe to use with the server
+ * online which pre-load activities going in parallel with server operations.
+ *
+ * This implementation is self-adjusting to any system workload and does not
+ * require any configuration parameters to optimize for initial system
+ * resources availability and/or any subsequent fluctuations.
+ */
+public class EntryCachePreloader
+  extends DirectoryThread
+  implements ServerShutdownListener
+{
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  /**
+   * Interrupt flag for the arbiter to terminate worker threads.
+   */
+  private AtomicBoolean interruptFlag = new AtomicBoolean(false);
+
+  /**
+   * Processed entries counter.
+   */
+  private AtomicLong processedEntries = new AtomicLong(0);
+
+  /**
+   * Progress report resolution.
+   */
+  private static final long progressInterval = 5000;
+
+  /**
+   * Default arbiter resolution time.
+   */
+  public static final long
+    PRELOAD_ARBITER_DEFAULT_SLEEP_TIME = 1000;
+
+  /**
+   * Effective arbiter resolution time.
+   */
+  private static long arbiterSleepTime;
+
+  /**
+   * Pre-load arbiter thread name.
+   */
+  private String preloadArbiterThreadName;
+
+  /**
+   * Pre-load arbiter thread.
+   */
+  private Thread preloadArbiterThread;
+
+  /**
+   * Worker threads.
+   */
+  private List<Thread> preloadThreads =
+    Collections.synchronizedList(
+    new LinkedList<Thread>());
+
+  /**
+   * DN Collector thread.
+   */
+  private EntryCacheDNCollector dnCollector =
+    new EntryCacheDNCollector();
+
+  /**
+   * This queue is for workers to take from.
+   */
+  private LinkedBlockingQueue<DN> dnQueue =
+      new LinkedBlockingQueue<DN>();
+
+  /**
+   * The number of bytes in a megabyte.
+   */
+  private static final int bytesPerMegabyte = 1024*1024;
+
+  /**
+   * Default constructor.
+   */
+  public EntryCachePreloader() {
+    super("Entry Cache Preload Arbiter");
+    preloadArbiterThreadName = getName();
+    DirectoryServer.registerShutdownListener(this);
+    // This should not be exposed as configuration
+    // parameter and is only useful for testing.
+    arbiterSleepTime = Long.getLong(
+      "org.opends.server.entrycache.preload.sleep",
+      PRELOAD_ARBITER_DEFAULT_SLEEP_TIME);
+  }
+
+  /**
+   * The Arbiter thread.
+   */
+  @Override
+  public void run() {
+    preloadArbiterThread = Thread.currentThread();
+    logError(NOTE_CACHE_PRELOAD_PROGRESS_START.get());
+    // Start DN collector thread first.
+    dnCollector.start();
+    // Kick off a single worker.
+    EntryCachePreloadWorker singleWorkerThread =
+      new EntryCachePreloadWorker();
+    singleWorkerThread.start();
+    preloadThreads.add(singleWorkerThread);
+    // Progress report timer task.
+    Timer timer = new Timer();
+    TimerTask progressTask = new TimerTask() {
+      // Persistent state restore progress report.
+      public void run() {
+        if (processedEntries.get() > 0) {
+          long freeMemory =
+            Runtime.getRuntime().freeMemory() / bytesPerMegabyte;
+          Message message = NOTE_CACHE_PRELOAD_PROGRESS_REPORT.get(
+            processedEntries.get(), freeMemory);
+          logError(message);
+        }
+      }
+    };
+    timer.scheduleAtFixedRate(progressTask, progressInterval,
+      progressInterval);
+    // Cycle to monitor progress and adjust workers.
+    long processedEntriesDeltaLow  = 0;
+    long processedEntriesDeltaHigh = 0;
+    long lastKnownProcessedEntries = 0;
+    try {
+      while (!dnQueue.isEmpty() || dnCollector.isAlive()) {
+        long processedEntriesCycle = processedEntries.get();
+        long processedEntriesDelta =
+          processedEntriesCycle - lastKnownProcessedEntries;
+        lastKnownProcessedEntries = processedEntriesCycle;
+        // Spawn another worker if scaling up.
+        if (processedEntriesDelta > processedEntriesDeltaHigh) {
+          processedEntriesDeltaLow = processedEntriesDeltaHigh;
+          processedEntriesDeltaHigh = processedEntriesDelta;
+          EntryCachePreloadWorker workerThread =
+            new EntryCachePreloadWorker();
+          workerThread.start();
+          preloadThreads.add(workerThread);
+        }
+        // Interrupt random worker if scaling down.
+        if (processedEntriesDelta < processedEntriesDeltaLow) {
+          processedEntriesDeltaHigh = processedEntriesDeltaLow;
+          processedEntriesDeltaLow = processedEntriesDelta;
+          // Leave at least one worker to progress.
+          if (preloadThreads.size() > 1) {
+            interruptFlag.set(true);
+          }
+        }
+        Thread.sleep(arbiterSleepTime);
+      }
+      // Join the collector.
+      dnCollector.join();
+      // Join all spawned workers.
+      for (Thread workerThread : preloadThreads) {
+        workerThread.join();
+      }
+      // Cancel progress report task and report done.
+      timer.cancel();
+      Message message = NOTE_CACHE_PRELOAD_PROGRESS_DONE.get(
+        processedEntries.get());
+      logError(message);
+    } catch (InterruptedException ex) {
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
+      }
+      // Interrupt the collector.
+      dnCollector.interrupt();
+      // Interrupt all preload threads.
+      for (Thread thread : preloadThreads) {
+        thread.interrupt();
+      }
+      logError(WARN_CACHE_PRELOAD_INTERRUPTED.get());
+    } finally {
+      // Kill the task in case of exception.
+      timer.cancel();
+    }
+  }
+
+  /**
+   * The worker thread.
+   */
+  private class EntryCachePreloadWorker extends DirectoryThread {
+    public EntryCachePreloadWorker() {
+      super("Entry Cache Preload Worker");
+    }
+    @Override
+    public void run() {
+      while (!dnQueue.isEmpty() || dnCollector.isAlive()) {
+        // Check if interrupted.
+        if (Thread.interrupted()) {
+          break;
+        }
+        if (interruptFlag.compareAndSet(true, false)) {
+          break;
+        }
+        // Dequeue the next entry DN.
+        try {
+          DN entryDN = dnQueue.take();
+          Lock readLock = null;
+          try {
+            // Acquire a read lock on the entry.
+            readLock = LockManager.lockRead(entryDN);
+            if (readLock == null) {
+              // It is cheaper to put this DN back on the
+              // queue then pick it up and process later.
+              dnQueue.add(entryDN);
+              continue;
+            }
+            // Even if getEntry() below fails the entry is
+            // still treated as a processed entry anyways.
+            processedEntries.getAndIncrement();
+            // getEntry() will trigger putEntryIfAbsent() to the
+            // cache if given entry is not in the cache already.
+            DirectoryServer.getEntry(entryDN);
+          } catch (DirectoryException ex) {
+            if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, ex);
+            }
+            Message message = ERR_CACHE_PRELOAD_ENTRY_FAILED.get(
+              entryDN.toNormalizedString(),
+              (ex.getCause() != null ? ex.getCause().getMessage() :
+                stackTraceToSingleLineString(ex)));
+            logError(message);
+          } finally {
+            LockManager.unlock(entryDN, readLock);
+          }
+        } catch (InterruptedException ex) {
+          break;
+        }
+      }
+      preloadThreads.remove(Thread.currentThread());
+    }
+  }
+
+  /**
+   * The Collector thread.
+   */
+  private class EntryCacheDNCollector extends DirectoryThread {
+    public EntryCacheDNCollector() {
+      super("Entry Cache Preload Collector");
+    }
+    @Override
+    public void run() {
+      Map<DN, Backend> baseDNMap =
+        DirectoryServer.getPublicNamingContexts();
+      Set<Backend> proccessedBackends = new HashSet<Backend>();
+      // Collect all DNs from every active public backend.
+      for (Backend backend : baseDNMap.values()) {
+        // Check if interrupted.
+        if (Thread.interrupted()) {
+          return;
+        }
+        if (!proccessedBackends.contains(backend)) {
+          proccessedBackends.add(backend);
+          try {
+            if (!backend.collectStoredDNs(dnQueue)) {
+              // DN collection is incomplete, likely
+              // due to some backend problem occured.
+              // Log an error message and carry on.
+              Message message =
+                ERR_CACHE_PRELOAD_COLLECTOR_FAILED.get(
+                backend.getBackendID());
+              logError(message);
+            }
+          } catch (UnsupportedOperationException ex) {
+            // Some backends dont have collectStoredDNs()
+            // method implemented, log a warning, skip
+            // such backend and continue.
+            if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, ex);
+            }
+            Message message =
+              WARN_CACHE_PRELOAD_BACKEND_FAILED.get(
+              backend.getBackendID());
+            logError(message);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public String getShutdownListenerName() {
+    return preloadArbiterThreadName;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void processServerShutdown(Message reason) {
+    if ((preloadArbiterThread != null) &&
+         preloadArbiterThread.isAlive()) {
+      // Interrupt the arbiter so it can interrupt
+      // the collector and all spawned workers.
+      preloadArbiterThread.interrupt();
+      try {
+        // This should be quick although if it
+        // gets interrupted it is no big deal.
+        preloadArbiterThread.join();
+      } catch (InterruptedException ex) {
+        if (debugEnabled()) {
+          TRACER.debugCaught(DebugLogLevel.ERROR, ex);
+        }
+      }
+    }
+    DirectoryServer.deregisterShutdownListener(this);
+  }
+}
diff --git a/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java b/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
index 74b3fd5..bace04f 100644
--- a/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
+++ b/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2007 Sun Microsystems, Inc.
+ *      Portions Copyright 2007-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.replication.server;
 import static org.opends.messages.BackendMessages.*;
@@ -40,6 +40,7 @@
 import java.io.IOException;
 import java.io.StringReader;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -1325,5 +1326,16 @@
       return new LDIFReader(config);
     }
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean collectStoredDNs(Collection<DN> storedDNs)
+    throws UnsupportedOperationException
+  {
+    throw new UnsupportedOperationException("Operation not supported.");
+  }
 }
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/PreloadEntryCacheTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/PreloadEntryCacheTestCase.java
new file mode 100644
index 0000000..c2e5f0b
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/PreloadEntryCacheTestCase.java
@@ -0,0 +1,235 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+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.core.DirectoryServer;
+import org.opends.server.types.Entry;
+import org.opends.server.util.ServerConstants;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+
+
+/**
+ * The entry cache pre-load test class.
+ */
+@Test(groups = { "entrycache", "slow" }, sequential=true)
+public class PreloadEntryCacheTestCase
+       extends ExtensionsTestCase
+{
+  /**
+   * Number of unique dummy test entries.
+   */
+  protected int NUMTESTENTRIES = 1000;
+
+  /**
+   * Dummy test entries.
+   */
+  protected ArrayList<Entry> testEntriesList;
+
+  /**
+   * Entry cache configuration instance.
+   */
+  protected EntryCacheCfg configuration;
+
+  /**
+   * Initialize the entry cache pre-load test.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @BeforeClass()
+  @SuppressWarnings("unchecked")
+  public void preloadEntryCacheTestInit()
+         throws Exception
+  {
+    // Ensure that the server is running.
+    TestCaseUtils.startServer();
+
+    // Initialize a backend with a base entry.
+    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+    // Configure the entry cache, use FileSystemEntryCache.
+    Entry cacheConfigEntry = 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: 1",
+      "ds-cfg-java-class: " +
+      "org.opends.server.extensions.FileSystemEntryCache",
+      "ds-cfg-enabled: true");
+    configuration = AdminTestCaseUtils.getConfiguration(
+      EntryCacheCfgDefn.getInstance(), cacheConfigEntry);
+
+    // Make some dummy test entries.
+    testEntriesList = new ArrayList<Entry>(NUMTESTENTRIES);
+    for(int i = 0; i < NUMTESTENTRIES; i++ ) {
+      Entry testEntry = TestCaseUtils.makeEntry(
+        "dn: uid=test" + Integer.toString(i) + ".user" + Integer.toString(i)
+         + ",dc=example,dc=com",
+        "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)
+      );
+      testEntriesList.add(testEntry);
+      TestCaseUtils.addEntry(testEntry);
+    }
+
+    // Initialize the cache reflecting on DirectoryServer
+    // and EntryCacheConfigManager.
+    final Field[] directoryFields =
+        DirectoryServer.class.getDeclaredFields();
+    for (int i = 0; i < directoryFields.length; ++i) {
+      if (directoryFields[i].getName().equals("entryCacheConfigManager")) {
+        directoryFields[i].setAccessible(true);
+        final Method[] cacheManagerMethods =
+          directoryFields[i].getType().getDeclaredMethods();
+        for (int j = 0; j < cacheManagerMethods.length; ++j) {
+          if (cacheManagerMethods[j].getName().equals(
+            "loadAndInstallEntryCache")) {
+            cacheManagerMethods[j].setAccessible(true);
+            cacheManagerMethods[j].invoke(directoryFields[i].get(
+              DirectoryServer.getInstance()),
+              configuration.getJavaClass(), configuration);
+          }
+        }
+      }
+    }
+
+    // Attempt to force GC to possibly free some memory.
+    System.gc();
+  }
+
+
+
+  /**
+   * Tests the entry cache pre-load.
+   */
+  @Test()
+  public void testEntryCachePreload()
+         throws Exception
+  {
+    // Make sure the entry cache is empty.
+    assertNull(toVerboseString(),
+      "Expected empty cache.  " + "Cache contents:" + ServerConstants.EOL +
+      toVerboseString());
+
+    // Start pre-load and wait for it to complete.
+    EntryCachePreloader preloadThread = new EntryCachePreloader();
+    preloadThread.start();
+    preloadThread.join();
+
+    // Check that all test entries are preloaded.
+    for(int i = 0; i < NUMTESTENTRIES; i++ ) {
+      assertNotNull(DirectoryServer.getEntryCache().getEntry(
+        testEntriesList.get(i).getDN()), "Expected to find " +
+        testEntriesList.get(i).getDN().toString() +
+        " in the cache.  Cache contents:" +
+        ServerConstants.EOL + toVerboseString());
+    }
+  }
+
+
+
+  /**
+   * Finalize the entry cache pre-load test.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @AfterClass()
+  public void preloadEntryCacheTestFini()
+         throws Exception
+  {
+    // Clear backend.
+    TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com");
+    // Sanity in-core restart.
+    TestCaseUtils.restartServer();
+    // Remove default FS cache JE environment.
+    FileSystemEntryCacheCfg config =
+      (FileSystemEntryCacheCfg) configuration;
+    TestCaseUtils.deleteDirectory(new File(config.getCacheDirectory()));
+  }
+
+
+
+  /**
+   * Reflection of the toVerboseString implementation method.
+   */
+  protected String toVerboseString()
+            throws Exception
+  {
+    final Method[] cacheMethods =
+        DirectoryServer.getEntryCache().getClass().getDeclaredMethods();
+
+    for (int i = 0; i < cacheMethods.length; ++i) {
+      if (cacheMethods[i].getName().equals("toVerboseString")) {
+        cacheMethods[i].setAccessible(true);
+        Object verboseString =
+          cacheMethods[i].invoke(DirectoryServer.getEntryCache(),
+          (Object[]) null);
+        return (String) verboseString;
+      }
+    }
+
+    return null;
+  }
+}

--
Gitblit v1.10.0