mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

coulbeck
21.23.2007 19fb5731ffbe883fe8375ee9b785984c8ccf1880
Migrate the BackendConfigManager to the new configuration framework.
6 files added
29 files modified
7420 ■■■■■ changed files
opends/resource/admin/abbreviations.xsl 10 ●●●● patch | view | raw | blame | history
opends/resource/admin/admin.xsd 2 ●●● patch | view | raw | blame | history
opends/resource/config/config.ldif 35 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif 13 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/BackendConfiguration.xml 173 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/BackupBackendConfiguration.xml 47 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/JEBackendConfiguration.xml 865 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml 17 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/RootDSEBackendConfiguration.xml 61 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/SchemaBackendConfiguration.xml 87 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/TaskBackendConfiguration.xml 75 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ConfigExceptionFactory.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ServerManagedObject.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/BackupBackend.java 221 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/MonitorBackend.java 187 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/RootDSEBackend.java 413 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/SchemaBackend.java 401 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/BackendImpl.java 317 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/Config.java 535 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/ConfigurableEnvironment.java 551 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/RootContainer.java 203 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/task/TaskBackend.java 424 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/BackendConfigManager.java 1942 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java 19 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java 3 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/AdminMessages.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/CoreMessages.java 6 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/JebMessages.java 458 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/UtilityMessages.java 2 ●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/resource/config-changes.ldif 164 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java 3 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaBackendTestCase.java 2 ●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java 113 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestEntryContainer.java 61 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestImportJob.java 3 ●●●●● patch | view | raw | blame | history
opends/resource/admin/abbreviations.xsl
@@ -33,20 +33,20 @@
  <!--
    Determines whether or not the provided word is a known abbreviation or
    acronym.
    @param value The word.
    @return Returns the string "true" if the word is an abbreviation.
  -->
  <xsl:template name="is-abbreviation">
    <xsl:param name="value" select="/.." />
    <xsl:value-of
      select="$value = 'aci' or $value = 'ip' or $value = 'ssl'
      select="$value = 'aci' or $value = 'ip' or $value = 'ssl'
              or $value = 'dn' or $value = 'rdn' or $value = 'jmx'
              or $value = 'smtp' or $value = 'http'  or $value = 'https'
              or $value = 'smtp' or $value = 'http'  or $value = 'https'
              or $value = 'ldap' or $value = 'ldaps' or $value = 'ldif'
              or $value = 'jdbc' or $value = 'tcp' or $value = 'tls'
              or $value = 'pkcs11' or $value = 'sasl' or $value = 'gssapi'
              or $value = 'md5' " />
              or $value = 'md5' or $value = 'je' or $value = 'dse' " />
  </xsl:template>
</xsl:stylesheet>
opends/resource/admin/admin.xsd
@@ -965,7 +965,7 @@
            Sizes can be specified using both decimal and binary units.
            For example, "1kb" represents 1000 bytes, and "1kib"
            represents 1024 bytes. Values must always specify the unit
            and can included a fractionaly part (e.g. 1.5mb). Both short
            and can include a fractional part (e.g. 1.5mb). Both short
            and long unit names are supported (e.g. "kb" and
            "kilobytes").
          </xsd:documentation>
opends/resource/config/config.ldif
@@ -120,6 +120,21 @@
ds-cfg-backend-import-thread-count: 8
ds-cfg-backend-entries-compressed: false
ds-cfg-backend-deadlock-retry-limit: 10
ds-cfg-database-cache-percent: 10
ds-cfg-database-cache-size: 0 megabytes
ds-cfg-database-txn-no-sync: false
ds-cfg-database-txn-write-no-sync: true
ds-cfg-database-run-cleaner: true
ds-cfg-database-cleaner-num-threads: 1
ds-cfg-database-cleaner-min-utilization: 75
ds-cfg-database-evictor-lru-only: true
ds-cfg-database-evictor-nodes-per-scan: 10
ds-cfg-database-log-file-max: 50 megabytes
ds-cfg-database-logging-file-handler-on: true
ds-cfg-database-logging-level: CONFIG
ds-cfg-database-checkpointer-bytes-interval: 20 megabytes
ds-cfg-database-checkpointer-wakeup-interval: 30 seconds
ds-cfg-database-lock-num-lock-tables: 19
dn: cn=Index,ds-cfg-backend-id=userRoot,cn=Backends,cn=config
objectClass: top
@@ -202,26 +217,6 @@
ds-cfg-index-attribute: entryuuid
ds-cfg-index-type: equality
dn: cn=JE Database,ds-cfg-backend-id=userRoot,cn=Backends,cn=config
objectClass: top
objectClass: ds-cfg-je-database
cn: JE Database
ds-cfg-database-cache-percent: 10
ds-cfg-database-cache-size: 0 megabytes
ds-cfg-database-txn-no-sync: false
ds-cfg-database-txn-write-no-sync: true
ds-cfg-database-run-cleaner: true
ds-cfg-database-cleaner-num-threads: 1
ds-cfg-database-cleaner-min-utilization: 75
ds-cfg-database-evictor-lru-only: true
ds-cfg-database-evictor-nodes-per-scan: 10
ds-cfg-database-log-file-max: 50 megabytes
ds-cfg-database-logging-file-handler-on: true
ds-cfg-database-logging-level: CONFIG
ds-cfg-database-checkpointer-bytes-interval: 20 megabytes
ds-cfg-database-checkpointer-wakeup-interval: 30 seconds
ds-cfg-database-lock-num-lock-tables: 19
dn: ds-cfg-backend-id=backup,cn=Backends,cn=config
objectClass: top
objectClass: ds-cfg-backend
opends/resource/schema/02-config.ldif
@@ -1210,7 +1210,16 @@
  ds-cfg-backend-import-temp-directory $ ds-cfg-backend-import-buffer-size $
  ds-cfg-backend-import-queue-size $ ds-cfg-backend-import-thread-count $
  ds-cfg-backend-entries-compressed $ ds-cfg-backend-deadlock-retry-limit $
  ds-cfg-backend-import-pass-size $ ds-cfg-backend-mode )
  ds-cfg-backend-import-pass-size $ ds-cfg-backend-mode $
  ds-cfg-database-cache-percent $
  ds-cfg-database-cache-size $ ds-cfg-database-txn-no-sync $
  ds-cfg-database-txn-write-no-sync $ ds-cfg-database-run-cleaner $
  ds-cfg-database-cleaner-min-utilization $ ds-cfg-database-evictor-lru-only $
  ds-cfg-database-evictor-nodes-per-scan $ ds-cfg-database-log-file-max $
  ds-cfg-database-logging-file-handler-on $ ds-cfg-database-logging-level $
  ds-cfg-database-checkpointer-bytes-interval $
  ds-cfg-database-checkpointer-wakeup-interval $
  ds-cfg-database-lock-num-lock-tables $ ds-cfg-database-cleaner-num-threads )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.7 NAME 'ds-cfg-je-database'
  SUP top STRUCTURAL MAY ( cn $ ds-cfg-database-cache-percent $
@@ -1624,7 +1633,7 @@
  NAME 'ds-task-initialize-remote-replica' SUP ds-task
  MUST ( ds-task-initialize-domain-dn $ ds-task-initialize-replica-server-id )
  MAY ( ds-task-processed-entry-count $ ds-task-unprocessed-entry-count )
  X-ORIGIN 'OpenDS Directory Server' )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.95
  NAME 'ds-cfg-dictionary-password-validator' SUP ds-cfg-password-validator
  STRUCTURAL MUST ( ds-cfg-dictionary-file $ ds-cfg-case-sensitive-validation $
opends/src/admin/defn/org/opends/server/admin/std/BackendConfiguration.xml
New file
@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8"?>
<adm:managed-object
     name="backend"
     plural-name="backends"
     package="org.opends.server.admin.std"
     xmlns:adm="http://www.opends.org/admin"
     xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    <adm:user-friendly-plural-name/> are responsible for providing access
    to the underlying data presented by the server. The data may be stored
    locally (e.g., in an embedded database), remotely (e.g., in an
    external system), or generated on the fly (e.g., calculated from other
    information that is available).
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.5</ldap:oid>
      <ldap:name>ds-cfg-backend</ldap:name>
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="backend-enabled"
    mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether the backend is enabled for use in the server.
    </adm:synopsis>
    <adm:description>
      If a backend is not enabled, then its contents will not be accessible
      when processing operations.
    </adm:description>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.13</ldap:oid>
        <ldap:name>ds-cfg-backend-enabled</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-class"
    mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      The fully-qualified name of the Java class that provides the
      backend implementation.
    </adm:synopsis>
    <adm:description>
      The specified class must be a subclass of the
      org.opends.server.api.Backend superclass.  The backend must be disabled
      and re-enabled for changes to the handler class to take effect.
    </adm:description>
    <adm:requires-admin-action>
      <adm:component-restart/>
    </adm:requires-admin-action>
    <adm:syntax>
      <adm:java-class>
        <adm:instance-of>org.opends.server.api.Backend</adm:instance-of>
      </adm:java-class>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.11</ldap:oid>
        <ldap:name>ds-cfg-backend-class</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-id"
    mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      Provides a name that will be used to identify the associated backend.
    </adm:synopsis>
    <adm:description>
      The name must be unique among all backends in the server.
    </adm:description>
    <adm:requires-admin-action>
      <adm:other>
        <adm:synopsis>
          The backend ID may not be altered after the backend is created in
          the server.
        </adm:synopsis>
      </adm:other>
    </adm:requires-admin-action>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.14</ldap:oid>
        <ldap:name>ds-cfg-backend-id</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-writability-mode"
    mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      Specifies the behavior that the backend should use when processing write
      operations.
    </adm:synopsis>
    <adm:description>
      A value of "enabled" will allow write operations to be
      performed in that backend (if the requested operation is valid, the user
      has permission to perform the operation, the backend supports that type
      of write operation, and the global writability mode property is also
      enabled).  A value of "disabled" will cause all write attempts to
      fail, and a value of "internal-only" will cause external write attempts
      to fail but will allow writes by replication and internal operations.
    </adm:description>
    <adm:syntax>
      <adm:enumeration>
        <adm:value name="enabled">
          <adm:synopsis>
            Allows write operations to be performed in that backend.
          </adm:synopsis>
        </adm:value>
        <adm:value name="disabled">
          <adm:synopsis>
            Causes all write attempts to fail.
          </adm:synopsis>
        </adm:value>
        <adm:value name="internal-only">
          <adm:synopsis>
            Causes external write attempts to fail but allows writes by
            replication and internal operations.
          </adm:synopsis>
        </adm:value>
      </adm:enumeration>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.162</ldap:oid>
        <ldap:name>ds-cfg-backend-writability-mode</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-base-dn"
    mandatory="true"
    multi-valued="true">
    <adm:synopsis>
      Specifies the base DN(s) for the data that the backend will handle.
    </adm:synopsis>
    <adm:description>
      A single backend may be responsible for one or more base DNs.  Note
      that no two backends may have the same base DN, although one backend
      may have a base DN that is below a base DN provided by another
      backend (similar to the use of sub-suffixes in the Sun Java System
      Directory Server).  Note that if any of the base DNs is subordinate
      to a base DN for another backend, then all base DNs for that backend
      must be subordinate to that same base DN.
    </adm:description>
    <adm:requires-admin-action>
      <adm:other>
        <adm:synopsis>
          No administrative action is required by default, although some action
          may be required on a per-backend basis before the new base DN may
          be used.
        </adm:synopsis>
      </adm:other>
    </adm:requires-admin-action>
    <adm:syntax>
      <adm:dn />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.10</ldap:oid>
        <ldap:name>ds-cfg-backend-base-dn</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/BackupBackendConfiguration.xml
New file
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<adm:managed-object name="backup-backend" plural-name="backup-backends"
  package="org.opends.server.admin.std"
  extends="backend"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    The backup backend provides read-only access to the set of backups that
    are available for the OpenDS Directory Server.
  </adm:synopsis>
  <adm:description>
    The backup backend is provided as a convenience feature that makes it
    easier to determine what backups are available to be restored if necessary.
    The org.opends.server.backends.BackupBackend class provides the
    implementation for this backend.
  </adm:description>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.68</ldap:oid>
      <ldap:name>ds-cfg-backup-backend</ldap:name>
      <ldap:superior>ds-cfg-backend</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="backup-directory"
    mandatory="true"
    multi-valued="true">
    <adm:synopsis>
      Specifies the path to a backup directory containing one or more backups
      for a particular backend. It may be either an absolute path or one that
      is relative to the base of the OpenDS Directory Server installation.
    </adm:synopsis>
    <adm:description>
      This is a multivalued attribute, and each value may specify a different
      backup directory if desired (e.g., one for each backend for which backups
      are taken).
    </adm:description>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.228</ldap:oid>
        <ldap:name>ds-cfg-backup-directory</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/JEBackendConfiguration.xml
New file
@@ -0,0 +1,865 @@
<?xml version="1.0" encoding="UTF-8"?>
<adm:managed-object name="je-backend" plural-name="je-backends"
  package="org.opends.server.admin.std"
  extends="backend"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    The primary backend provided by the OpenDS Directory Server uses the
    Berkeley DB Java Edition to store user-provided data in a local repository.
    It is the traditional "directory server" backend and is similar to the
    backends provided by the Sun Java System Directory Server.
  </adm:synopsis>
  <adm:description>
    The <adm:user-friendly-name/> stores the entries in an encoded form and
    also provides indexes that can be used to quickly locate target entries
    based on different kinds of criteria.
    The org.opends.server.backends.jeb.BackendImpl class provides the
    implementation for this backend, and therefore should be used as the
    value of the ds-cfg-backend-class-name property.
  </adm:description>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.6</ldap:oid>
      <ldap:name>ds-cfg-je-backend</ldap:name>
      <ldap:superior>ds-cfg-backend</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="backend-deadlock-retry-limit"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the number of times that the server should retry an attempted
      operation in the backend if a deadlock results from two concurrent
      requests that interfere with each other in a conflicting manner.
    </adm:synopsis>
    <adm:description>
      A value of "0" indicates no limit.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>10</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer  lower-limit="0" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.267</ldap:oid>
        <ldap:name>ds-cfg-backend-deadlock-retry-limit</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-directory"
    mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      Specifies the path to the filesystem directory that will be used to hold
      the Berkeley DB Java Edition database files containing the data for this
      backend.
    </adm:synopsis>
    <adm:description>
      The path may be either an absolute path or a path relative to the
      directory containing the base of the OpenDS Directory Server installation.
      The path may be any valid directory path in which the server has
      appropriate permissions to read and write files and has sufficient space
      to hold the database contents.
    </adm:description>
    <adm:requires-admin-action>
      <adm:component-restart/>
    </adm:requires-admin-action>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.12</ldap:oid>
        <ldap:name>ds-cfg-backend-directory</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-entries-compressed"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether the backend should attempt to compress entries before
      storing them in the database.
    </adm:synopsis>
    <adm:description>
      Note that this property applies only to the entries themselves
      and does not impact the index data.  Further, the effectiveness of the
      compression will be based on the type of data contained in the entry.
    </adm:description>
    <adm:requires-admin-action>
      <adm:other>
        <adm:synopsis>
          Changes to this setting will only take effect for writes that occur
          after the change is made.  It will not be retroactively applied to
          existing data.
        </adm:synopsis>
      </adm:other>
    </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:oid>1.3.6.1.4.1.26027.1.1.266</ldap:oid>
        <ldap:name>ds-cfg-backend-entries-compressed</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-import-buffer-size"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the amount of memory that should be used as an internal
      buffer for index information when processing an LDIF import.
    </adm:synopsis>
    <adm:requires-admin-action>
      <adm:other>
        <adm:synopsis>
          No admin action required, although changes will not take effect for
          any import that may already be in progress.
        </adm:synopsis>
      </adm:other>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>256mb</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:size lower-limit="10mb"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.263</ldap:oid>
        <ldap:name>ds-cfg-backend-import-buffer-size</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-import-pass-size"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum number of entries that should be imported in each
      import pass.
    </adm:synopsis>
    <adm:description>
      An import pass consists of the processing required to import a set of
      entries as well as the index post-processing required to index those
      entries.  A value of zero for this property indicates that all entries
      should be processed in a single pass, which is the recommended
      configuration for most deployments, although a non-zero value may be
      required when importing a very large number of entries if the amount
      of memory required for index post-processing exceeds the total amount
      available to the server.
    </adm:description>
    <adm:requires-admin-action>
      <adm:other>
        <adm:synopsis>
          No admin action required, although changes will not take effect for
          any import that may already be in progress.
        </adm:synopsis>
      </adm:other>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer  lower-limit="0"  upper-limit="2147483647" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.277</ldap:oid>
        <ldap:name>ds-cfg-backend-import-pass-size</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-import-queue-size"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the size (in number of entries) of the queue that will be used
      to hold the entries read during an LDIF import.
    </adm:synopsis>
    <adm:requires-admin-action>
      <adm:other>
        <adm:synopsis>
          No admin action required, although changes will not take effect for
          any import that may already be in progress.
        </adm:synopsis>
      </adm:other>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>100</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer  lower-limit="1" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.264</ldap:oid>
        <ldap:name>ds-cfg-backend-import-queue-size</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-import-temp-directory"
    mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      Specifies the location of the directory that will be used for the
      files used to hold temporary information that will be used during the
      index post-processing phase of an LDIF import.
    </adm:synopsis>
    <adm:description>
      The specified directory will only be used while an import is in progress
      and the files created in this directory will be deleted as they are
      processed. It may be an absolute path or one that is relative to the
      instance root directory.
    </adm:description>
    <adm:requires-admin-action>
      <adm:other>
        <adm:synopsis>
          No admin action required, although changes will not take effect for
          any import that may already be in progress.
        </adm:synopsis>
      </adm:other>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:undefined/>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.262</ldap:oid>
        <ldap:name>ds-cfg-backend-import-temp-directory</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-import-thread-count"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the number of threads that will be used for concurrent
      processing during an LDIF import.
    </adm:synopsis>
    <adm:description>
      This should generally be a small multiple (e.g., 2x) of the number of CPUs
      in the system for a traditional system, or equal to the number of CPU
      strands for a CMT system.
    </adm:description>
    <adm:requires-admin-action>
      <adm:other>
        <adm:synopsis>
          No admin action required, although changes will not take effect for
          any import that may already be in progress.
        </adm:synopsis>
      </adm:other>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>8</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer  lower-limit="1" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.265</ldap:oid>
        <ldap:name>ds-cfg-backend-import-thread-count</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-index-entry-limit"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum number of entries that will be allowed to
      match a given index key before that particular index key is no longer
      maintained (i.e., it is analogous to the ALL IDs threshold in the Sun
      Java System Directory Server). Note that this is the default limit for
      the backend, and it may be overridden on a per-attribute basis.
    </adm:synopsis>
    <adm:description>
      Note that this is the default limit for the backend, and it may be
      overridden on a per-attribute basis. A value of 0 means there is no limit.
    </adm:description>
    <adm:requires-admin-action>
      <adm:other>
        <adm:synopsis>
          No admin action is required, although if any index keys have already
          reached this limit, indexes will need to be rebuilt before they will
          be allowed to use the new limit.
        </adm:synopsis>
      </adm:other>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>4000</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer  lower-limit="0" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.15</ldap:oid>
        <ldap:name>ds-cfg-backend-index-entry-limit</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-mode"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the permissions that should be applied to the directory
      containing the server database files.  They should be expressed as
      three-digit octal values, which is the traditional representation for
      UNIX file permissions.
    </adm:synopsis>
    <adm:description>
      The three digits represent the permissions that will be available for the
      directory's owner, group members, and other users (in that order), and
      each digit is the octal representation of the read, write, and execute
      bits.
      Note that this only impacts permissions on the database directory and
      not on the files written into that directory. On UNIX systems, the
      user's umask will control permissions given to the database files.
    </adm:description>
    <adm:requires-admin-action>
      <adm:server-restart/>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>700</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string>
        <adm:pattern>
          <adm:regex>7[0-7][0-7]</adm:regex>
          <adm:synopsis>
            Any octal value between 700 and 777 (the owner must always have
            read, write, and execute permissions on the directory).
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.287</ldap:oid>
        <ldap:name>ds-cfg-backend-mode</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-preload-time-limit"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the length of time that the backend will be allowed to
      spend "pre-loading" data when it is initialized.
    </adm:synopsis>
    <adm:description>
      The pre-load process may be used to pre-populate the database cache so
      that it can be more quickly available when the server is processing
      requests.  A duration of zero means there will be no pre-load.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0s</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration base-unit="ms" lower-limit="0"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.261</ldap:oid>
        <ldap:name>ds-cfg-backend-preload-time-limit</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="backend-subtree-delete-size-limit"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum number of entries that may be deleted from the
      backend when using the subtree delete control.
    </adm:synopsis>
    <adm:description>
      If a subtree delete operation targets a subtree with more than this
      number of entries, then multiple passes may be required to remove all
      entries in that subtree.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>100000</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer  lower-limit="0" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.16</ldap:oid>
        <ldap:name>ds-cfg-backend-subtree-delete-size-limit</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-cache-percent"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      The percentage of JVM memory to allocate to the database cache.
    </adm:synopsis>
    <adm:description>
      Specifies the percentage of memory available to the JVM that should
      be used for caching database contents. Note that this will only be used
      if the value of the database-cache-size property is set to "0 MB".
      Otherwise, the value of that property will be used instead to control
      the cache size configuration.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>10</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer  lower-limit="1"  upper-limit="90" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.25</ldap:oid>
        <ldap:name>ds-cfg-database-cache-percent</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-cache-size"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      The amount of JVM memory to allocate to the database cache.
    </adm:synopsis>
    <adm:description>
      Specifies the amount of memory that should be used for caching database
      contents. A value of "0 MB" indicates that the database-cache-percent
      property should be used instead to specify the cache size.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0 MB</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:size lower-limit="0 MB" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.27</ldap:oid>
        <ldap:name>ds-cfg-database-cache-size</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-cleaner-min-utilization"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the minimum percentage of "live" data that the database cleaner
      will attempt to keep in database log files.
    </adm:synopsis>
    <adm:description>
      If the amount of live data in any database log file drops below this
      percentage, then the cleaner will move the remaining live data in that
      file to the end of the database and will delete the original file in
      order to keep the database relatively compact.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>75</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer  lower-limit="0"  upper-limit="90" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.26</ldap:oid>
        <ldap:name>ds-cfg-database-cleaner-min-utilization</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-run-cleaner"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      This indicates whether the database cleaner threads should be enabled.
    </adm:synopsis>
    <adm:description>
      The cleaner threads will be used to periodically compact the database by
      identifying database files with a low (i.e., less than the amount
      specified by the database-cleaner-min-utilization property)
      percentage of live data, moving the remaining live data to the end of the
      log and deleting that file.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>true</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.28</ldap:oid>
        <ldap:name>ds-cfg-database-run-cleaner</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-evictor-lru-only"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether the database should evict existing data from the cache
      based on an LRU policy (where the least recently used information will be
      evicted first).
    </adm:synopsis>
    <adm:description>
      If the value of this configuration property is set to "false", then
      eviction will prefer to keep internal nodes of the underlying Btree in
      the cache over leaf notes, even if the leaf nodes have been accessed
      more recently, which may be a better configuration for databases in
      which only a very small portion of the data is cached.
    </adm:description>
    <adm:requires-admin-action>
      <adm:component-restart/>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>true</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.268</ldap:oid>
        <ldap:name>ds-cfg-database-evictor-lru-only</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-evictor-nodes-per-scan"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the number of Btree nodes that should be evicted from the
      cache in a single pass if it is determined that it is necessary to
      free existing data in order to make room for new information.
    </adm:synopsis>
    <adm:description>
      Changes to this property do not take effect until the backend is
      restarted.  It is recommended that you also change this property when you
      set database-evictor-lru-only to false. This setting controls the number
      of Btree nodes that are considered, or sampled, each time a node is
      evicted. A setting of 100 often produces good results, but this may vary
      from application to application. The larger the nodesPerScan, the more
      accurate the algorithm. However, setting it too high is detrimental;
      the need to consider larger numbers of nodes for each eviction may
      delay the completion of a given database operation, which will impact
      the response time of the application thread.
    </adm:description>
    <adm:requires-admin-action>
      <adm:component-restart/>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>10</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer  lower-limit="1"  upper-limit="1000" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.269</ldap:oid>
        <ldap:name>ds-cfg-database-evictor-nodes-per-scan</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-log-file-max"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum size that may be used for a database log file.
    </adm:synopsis>
    <adm:requires-admin-action>
      <adm:component-restart/>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>50mb</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:size lower-limit="1mb" upper-limit="4gib"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.270</ldap:oid>
        <ldap:name>ds-cfg-database-log-file-max</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-logging-file-handler-on"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether the database should maintain a je.info file in the same
      directory as the database log directory. This file will contain
      information about the internal processing performed by the underlying
      database.
    </adm:synopsis>
    <adm:requires-admin-action>
      <adm:component-restart/>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>true</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.271</ldap:oid>
        <ldap:name>ds-cfg-database-logging-file-handler-on</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-logging-level"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      This specifies the log level that should be used by the database when
      it is writing information into the je.info file.
    </adm:synopsis>
    <adm:description>
      The database trace logging level is (in increasing order of verbosity)
      chosen from: OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL.
    </adm:description>
    <adm:requires-admin-action>
      <adm:component-restart/>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>CONFIG</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.272</ldap:oid>
        <ldap:name>ds-cfg-database-logging-level</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-checkpointer-bytes-interval"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum number of bytes that may be written to the database
      before it will be forced to perform a checkpoint.
    </adm:synopsis>
    <adm:description>
      This can be used to bound the recovery time that may be required if the
      database environment is opened without having been properly closed.
      If this propertyis set to a non-zero value, the checkpointer wakeup
      interval is not used.  To use time based checkpointing, set this
      property to zero.
    </adm:description>
    <adm:requires-admin-action>
      <adm:server-restart/>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>20mb</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:size lower-limit="0b" upper-limit="9223372036854775807b"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.273</ldap:oid>
        <ldap:name>ds-cfg-database-checkpointer-bytes-interval</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-checkpointer-wakeup-interval"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum length of time that may pass between checkpoints.
    </adm:synopsis>
    <adm:description>
      Note that this will only be used if the value of the checkpointer bytes
      interval is zero.
    </adm:description>
    <adm:requires-admin-action>
      <adm:component-restart/>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>30s</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration base-unit="s" lower-limit="1" upper-limit="4294"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.274</ldap:oid>
        <ldap:name>ds-cfg-database-checkpointer-wakeup-interval</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-lock-num-lock-tables"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      This specifies the number of lock tables that should be used by the
      underlying database.
    </adm:synopsis>
    <adm:description>
      This can be particularly important to help improve scalability by
      avoiding contention on systems with large numbers of CPUs. The value of
      this configuration property should be set to a prime number that is less
      than or equal to the number of worker threads configured for use in the
      server.
    </adm:description>
    <adm:requires-admin-action>
      <adm:component-restart/>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>19</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer  lower-limit="1"  upper-limit="32767" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.275</ldap:oid>
        <ldap:name>ds-cfg-database-lock-num-lock-tables</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-cleaner-num-threads"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the number of threads that the backend should maintain to
      keep the database log files at or near the desired utilization.
    </adm:synopsis>
    <adm:description>
      In environments with high write throughput, multiple cleaner threads may
      be required to maintain the desired utilization.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>1</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer  lower-limit="1" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.284</ldap:oid>
        <ldap:name>ds-cfg-database-cleaner-num-threads</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-txn-no-sync"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether database writes should be primarily written to an
      internal buffer but not immediately written to disk.
    </adm:synopsis>
    <adm:description>
      Setting the value of this configuration attribute to "true" may improve
      write performance but could cause some number of the most recent changes
      to be lost if the OpenDS Directory Server or the underlying JVM exits
      abnormally, or if an OS or hardware failure occurs (a behavior similar
      to running with transaction durability disabled in the Sun Java System
      Directory Server).
    </adm:description>
    <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:oid>1.3.6.1.4.1.26027.1.1.29</ldap:oid>
        <ldap:name>ds-cfg-database-txn-no-sync</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="database-txn-write-no-sync"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether the database should synchronously flush data as it is
      written to disk.
    </adm:synopsis>
    <adm:description>
      If this value is set to "false", then all data written to disk will be
      synchronously flushed to persistent storage and thereby providing full
      durability. If it is set to "true", then data may be cached for a period
      of time by the underlying operating system before actually being written
      to disk. This may improve performance, but could cause some number of
      the most recent changes to be lost in the event of an underlying OS or
      hardware failure (but not in the case that the OpenDS Directory Server
      or the JVM exits abnormally).
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>true</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.30</ldap:oid>
        <ldap:name>ds-cfg-database-txn-write-no-sync</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
@@ -93,5 +93,22 @@
      </ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:relation name="backend">
    <adm:one-to-many />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>
        cn=Backends,cn=config
      </ldap:rdn-sequence>
      <ldap:naming-attribute>ds-cfg-backend-id</ldap:naming-attribute>
    </adm:profile>
  </adm:relation>
  <adm:relation name="root-dse-backend">
    <adm:one-to-one />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>
        cn=config
      </ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:product-name>OpenDS Directory Server</adm:product-name>
</adm:root-managed-object>
opends/src/admin/defn/org/opends/server/admin/std/RootDSEBackendConfiguration.xml
New file
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<adm:managed-object name="root-dse-backend" plural-name="root-dse-backends"
  package="org.opends.server.admin.std"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    This class defines a backend to hold the Directory Server root DSE.
  </adm:synopsis>
  <adm:description>
    This is a special meta-backend that will dynamically generate the root DSE
    entry for base-level searches, and will simply redirect to other backends
    for operations in other scopes.
  </adm:description>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.42</ldap:oid>
      <ldap:name>ds-cfg-root-dse</ldap:name>
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="subordinate-base-dn"
    mandatory="false"
    multi-valued="true">
    <adm:synopsis>
      Specifies the set of base DNs that will be used for singleLevel,
      wholeSubtree, and subordinateSubtree searches based at the root DSE.  If
      this is not provided, then the set of all user-defined suffixes will be
      used.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:undefined/>
    </adm:default-behavior>
    <adm:syntax>
      <adm:dn />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.122</ldap:oid>
        <ldap:name>ds-cfg-subordinate-base-dn</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="show-all-attributes"
    mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether all attributes in the root DSE should be treated like
      user attributes (and therefore returned to clients by default)
      regardless of the Directory Server schema configuration.
    </adm:synopsis>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.141</ldap:oid>
        <ldap:name>ds-cfg-show-all-attributes</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/SchemaBackendConfiguration.xml
New file
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<adm:managed-object name="schema-backend" plural-name="schema-backends"
  package="org.opends.server.admin.std"
  extends="backend"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    The schema backend provides access to the OpenDS Directory Server schema
    information, including the attribute types, object classes, attribute
    syntaxes, matching rules, matching rule uses, DIT content rules, and
    DIT structure rules that it contains.
  </adm:synopsis>
  <adm:description>
    The server will allow modify operations in this backend to alter the server
    schema definitions. The org.opends.server.backends.SchemaBackend class
    provides the implementation for this backend.
    The configuration entry for this backend is based on the
    ds-cfg-schema-backend structural object class. Note that any attribute
    types included in this entry that are not included in this object class
    (or the parent ds-cfg-backend class) will appear directly in the schema
    entry.
  </adm:description>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.9</ldap:oid>
      <ldap:name>ds-cfg-schema-backend</ldap:name>
      <ldap:superior>ds-cfg-backend</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="schema-entry-dn"
    mandatory="false"
    multi-valued="true">
    <adm:synopsis>
      This defines the base DN(s) at which the schema information will be
      published, in addition to the value included in the ds-cfg-backend-base-dn
      configuration attribute.
    </adm:synopsis>
    <adm:description>
      The value provided in the ds-cfg-backend-base-dn configuration attribute
      is the only one that will appear in the subschemaSubentry operational
      attribute of the server's root DSE (which is necessary because that is a
      single-valued attribute) and as a virtual attribute in other entries,
      but the ds-cfg-schema-entry-dn attribute may be used to make the schema
      information available in other locations as well in case certain client
      applications have been hard-coded to expect the schema to reside in a
      specific location.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>cn=schema</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:dn />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.113</ldap:oid>
        <ldap:name>ds-cfg-schema-entry-dn</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="show-all-attributes"
    mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether to treat all attributes in the schema entry as if they
      were user attributes regardless of their configuration.
    </adm:synopsis>
    <adm:description>
      This may provide compatibility with some applications that expect schema
      attributes like attributeType and objectClasses to be included by default
      even if they are not requested.  Note that the ldapSyntaxes attribute
      will always be treated as operational in order to avoid problems with
      attempts to modify the schema over protocol.
    </adm:description>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.141</ldap:oid>
        <ldap:name>ds-cfg-show-all-attributes</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/TaskBackendConfiguration.xml
New file
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<adm:managed-object name="task-backend" plural-name="task-backends"
  package="org.opends.server.admin.std"
  extends="backend"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    The task backend provides a mechanism for processing tasks in the
    OpenDS Directory Server. Tasks are intended to provide access to certain
    types of administrative functions in the server that may not otherwise be
    convenient to perform remotely.
  </adm:synopsis>
  <adm:description>
    Tasks that are currently available for use provide the ability to backup
    and restore backends, to import and export LDIF files, and to stop and
    restart the server. The details of the task to perform are held in an
    entry that is added below the root of the task backend, and then the
    task backend is responsible for decoding that task entry and ensuring
    that it is processed as requested. Tasks may be invoked immediately,
    but they may also be scheduled for execution at some future time.
    It is also expected that task backend will be given the ability process
    recurring tasks, which can be used to help ensure that maintenance
    operations (e.g., backups) are performed automatically on a regular basis.
    The org.opends.server.backends.task.TaskBackend class provides the entry
    point for the task backend implementation.
  </adm:description>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.10</ldap:oid>
      <ldap:name>ds-cfg-task-backend</ldap:name>
      <ldap:superior>ds-cfg-backend</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="task-backing-file"
    mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      This specifies the path to the backing file for storing information about
      the tasks configured in the server. It may be either an absolute path or
      a path that is relative to the base of the OpenDS Directory Server
      instance.
    </adm:synopsis>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.97</ldap:oid>
        <ldap:name>ds-cfg-task-backing-file</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="task-retention-time"
    mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      This specifies the length of time that task entries should be retained
      after processing on the associated task has been completed.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>24 hours</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.106</ldap:oid>
        <ldap:name>ds-cfg-task-retention-time</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/server/org/opends/server/admin/server/ConfigExceptionFactory.java
@@ -41,7 +41,7 @@
/**
 * A utility class for converting admin exceptions to config exceptions.
 */
final class ConfigExceptionFactory {
public final class ConfigExceptionFactory {
  // The singleton instance.
  private static final ConfigExceptionFactory INSTANCE =
opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
@@ -178,7 +178,8 @@
   *           If one or more of the managed object's properties could
   *           not be decoded.
   */
  static <S extends Configuration> ServerManagedObject<? extends S> decode(
  public static <S extends Configuration>
  ServerManagedObject<? extends S> decode(
      ManagedObjectPath path,
      AbstractManagedObjectDefinition<?, S> definition,
      final ConfigEntry configEntry)
opends/src/server/org/opends/server/backends/BackupBackend.java
@@ -29,28 +29,18 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.*;
import org.opends.server.api.Backend;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.BackendConfigManager;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.schema.BooleanSyntax;
import org.opends.server.schema.GeneralizedTimeSyntax;
@@ -82,7 +72,9 @@
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.BackupBackendCfg;
import org.opends.server.admin.std.meta.BackupBackendCfgDefn;
/**
@@ -95,10 +87,10 @@
 */
public class BackupBackend
       extends Backend
       implements ConfigurableComponent
       implements ConfigurationChangeListener<BackupBackendCfg>
{
  // The DN of the configuration entry for this backend.
  private DN configEntryDN;
  // The current configuration state.
  private BackupBackendCfg currentConfig;
  // The DN for the base backup entry.
  private DN backupBaseDN;
@@ -135,20 +127,7 @@
  /**
   * Initializes this backend based on the information in the provided
   * configuration entry.
   *
   * @param  configEntry  The configuration entry that contains the information
   *                      to use to initialize this backend.
   * @param  baseDNs      The set of base DNs that have been configured for this
   *                      backend.
   *
   * @throws  ConfigException  If an unrecoverable problem arises in the
   *                           process of performing the initialization.
   *
   * @throws  InitializationException  If a problem occurs during initialization
   *                                   that is not related to the server
   *                                   configuration.
   * {@inheritDoc}
   */
  public void initializeBackend(ConfigEntry configEntry, DN[] baseDNs)
         throws ConfigException, InitializationException
@@ -162,7 +141,7 @@
      throw new ConfigException(msgID, message);
    }
    configEntryDN = configEntry.getDN();
    BackupBackendCfg cfg = getBackupBackendCfg(configEntry);
    // Create the set of base DNs that we will handle.  In this case, it's just
@@ -188,39 +167,11 @@
    // Determine the set of backup directories that we will use by default.
    int msgID = MSGID_BACKUP_DESCRIPTION_BACKUP_DIR_LIST;
    StringConfigAttribute backupDirStub =
         new StringConfigAttribute(ATTR_BACKUP_DIR_LIST, getMessage(msgID),
                                   true, true, false);
    try
    Set<String> values = cfg.getBackupDirectory();
    backupDirectories = new LinkedHashSet<File>(values.size());
    for (String s : values)
    {
      StringConfigAttribute backupDirAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(backupDirStub);
      if (backupDirAttr == null)
      {
        backupDirectories = new LinkedHashSet<File>();
      }
      else
      {
        List<String> values = backupDirAttr.activeValues();
        backupDirectories = new LinkedHashSet<File>(values.size());
        for (String s : values)
        {
          backupDirectories.add(getFileForPath(s));
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_BACKUP_CANNOT_DETERMINE_BACKUP_DIR_LIST;
      String message = getMessage(msgID, stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
      backupDirectories.add(getFileForPath(s));
    }
@@ -263,8 +214,9 @@
    supportedFeatures = new HashSet<String>(0);
    // Register with the Directory Server as a configurable component.
    DirectoryServer.registerConfigurableComponent(this);
    // Register this as a change listener.
    currentConfig = cfg;
    cfg.addBackupChangeListener(this);
    // Register the backup base as a private suffix.
@@ -279,7 +231,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
      int msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
      String message = getMessage(msgID, backupBaseDN.toString(),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
@@ -301,7 +253,7 @@
   */
  public void finalizeBackend()
  {
    DirectoryServer.deregisterConfigurableComponent(this);
    currentConfig.removeBackupChangeListener(this);
    try
    {
@@ -1310,63 +1262,11 @@
  /**
   * Retrieves the DN of the configuration entry with which this component is
   * associated.
   *
   * @return  The DN of the configuration entry with which this component is
   *          associated.
   * {@inheritDoc}
   */
  public DN getConfigurableComponentEntryDN()
  {
    return configEntryDN;
  }
  /**
   * Retrieves the set of configuration attributes that are associated with this
   * configurable component.
   *
   * @return  The set of configuration attributes that are associated with this
   *          configurable component.
   */
  public List<ConfigAttribute> getConfigurationAttributes()
  {
    LinkedList<ConfigAttribute> attrs = new LinkedList<ConfigAttribute>();
    ArrayList<String> backupDirs =
         new ArrayList<String>(backupDirectories.size());
    for (File f : backupDirectories)
    {
      backupDirs.add(f.getAbsolutePath());
    }
    int msgID = MSGID_BACKUP_DESCRIPTION_BACKUP_DIR_LIST;
    attrs.add(new StringConfigAttribute(ATTR_BACKUP_DIR_LIST, getMessage(msgID),
                                        true, true, false, backupDirs));
    return attrs;
  }
  /**
   * Indicates whether the provided configuration entry has an acceptable
   * configuration for this component.  If it does not, then detailed
   * information about the problem(s) should be added to the provided list.
   *
   * @param  configEntry          The configuration entry for which to make the
   *                              determination.
   * @param  unacceptableReasons  A list that can be used to hold messages about
   *                              why the provided entry does not have an
   *                              acceptable configuration.
   *
   * @return  <CODE>true</CODE> if the provided entry has an acceptable
   *          configuration for this component, or <CODE>false</CODE> if not.
   */
  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
                                            List<String> unacceptableReasons)
  public boolean isConfigurationChangeAcceptable(
       BackupBackendCfg configEntry,
       List<String> unacceptableReasons)
  {
    // We'll accept anything here.  The only configurable attribute is the
    // default set of backup directories, but that doesn't require any
@@ -1375,67 +1275,25 @@
  }
  /**
   * Makes a best-effort attempt to apply the configuration contained in the
   * provided entry.  Information about the result of this processing should be
   * added to the provided message list.  Information should always be added to
   * this list if a configuration change could not be applied.  If detailed
   * results are requested, then information about the changes applied
   * successfully (and optionally about parameters that were not changed) should
   * also be included.
   *
   * @param  configEntry      The entry containing the new configuration to
   *                          apply for this component.
   * @param  detailedResults  Indicates whether detailed information about the
   *                          processing should be added to the list.
   *
   * @return  Information about the result of the configuration update.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
                                                  boolean detailedResults)
  public ConfigChangeResult applyConfigurationChange(
       BackupBackendCfg configEntry)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    int msgID = MSGID_BACKUP_DESCRIPTION_BACKUP_DIR_LIST;
    StringConfigAttribute backupDirStub =
         new StringConfigAttribute(ATTR_BACKUP_DIR_LIST, getMessage(msgID),
                                   true, true, false);
    try
    Set<String> values = configEntry.getBackupDirectory();
    backupDirectories = new LinkedHashSet<File>(values.size());
    for (String s : values)
    {
      StringConfigAttribute backupDirAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(backupDirStub);
      if (backupDirAttr == null)
      {
        backupDirectories = new LinkedHashSet<File>();
      }
      else
      {
        List<String> values = backupDirAttr.activeValues();
        backupDirectories = new LinkedHashSet<File>(values.size());
        for (String s : values)
        {
          backupDirectories.add(getFileForPath(s));
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_BACKUP_CANNOT_DETERMINE_BACKUP_DIR_LIST;
      messages.add(getMessage(msgID, stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      backupDirectories.add(getFileForPath(s));
    }
    currentConfig = configEntry;
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
@@ -1454,5 +1312,20 @@
         new AttributeValue(rdnAttrType, rdnStringValue);
    return parentDN.concat(RDN.create(rdnAttrType, attrValue));
  }
  /**
   * Gets the backup backend configuration corresponding to a backup
   * backend config entry.
   *
   * @param configEntry A backup backend config entry.
   * @return Returns the backup backend configuration.
   * @throws ConfigException If the config entry could not be decoded.
   */
  static BackupBackendCfg getBackupBackendCfg(ConfigEntry configEntry)
      throws ConfigException {
    return BackendConfigManager.getConfiguration(
         BackupBackendCfgDefn.getInstance(), configEntry);
  }
}
opends/src/server/org/opends/server/backends/MonitorBackend.java
@@ -33,13 +33,10 @@
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import org.opends.server.api.Backend;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.api.MonitorProvider;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AddOperation;
@@ -48,6 +45,7 @@
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.BackendConfigManager;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
@@ -77,9 +75,11 @@
import org.opends.server.types.DebugLogLevel;
import static org.opends.server.messages.BackendMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.ConfigMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.BackendCfg;
/**
@@ -90,7 +90,7 @@
 */
public class MonitorBackend
       extends Backend
       implements ConfigurableComponent
       implements ConfigurationChangeListener<BackendCfg>
{
  // The set of user-defined attributes that will be included in the base
  // monitor entry.
@@ -102,6 +102,9 @@
  // The DN of the configuration entry for this backend.
  private DN configEntryDN;
  // The current configuration state.
  private BackendCfg currentConfig;
  // The DN for the base monitor entry.
  private DN baseMonitorDN;
@@ -131,20 +134,7 @@
  /**
   * Initializes this backend based on the information in the provided
   * configuration entry.
   *
   * @param  configEntry  The configuration entry that contains the information
   *                      to use to initialize this backend.
   * @param  baseDNs      The set of base DNs that have been configured for this
   *                      backend.
   *
   * @throws  ConfigException  If an unrecoverable problem arises in the
   *                           process of performing the initialization.
   *
   * @throws  InitializationException  If a problem occurs during initialization
   *                                   that is not related to the server
   *                                   configuration.
   * {@inheritDoc}
   */
  public void initializeBackend(ConfigEntry configEntry, DN[] baseDNs)
         throws ConfigException, InitializationException
@@ -158,6 +148,7 @@
      throw new ConfigException(msgID, message);
    }
    BackendCfg cfg = BackendConfigManager.getBackendCfg(configEntry);
    configEntryDN = configEntry.getDN();
@@ -227,7 +218,8 @@
    // Register with the Directory Server as a configurable component.
    DirectoryServer.registerConfigurableComponent(this);
    currentConfig = cfg;
    cfg.addChangeListener(this);
    // Register the monitor base as a private suffix.
@@ -264,7 +256,7 @@
   */
  public void finalizeBackend()
  {
    DirectoryServer.deregisterConfigurableComponent(this);
    currentConfig.removeChangeListener(this);
    try
    {
@@ -955,17 +947,7 @@
  /**
   * Exports the contents of this backend to LDIF.  This method should only be
   * called if <CODE>supportsLDIFExport</CODE> returns <CODE>true</CODE>.  Note
   * that the server will not explicitly initialize this backend before calling
   * this method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  baseDNs       The set of base DNs configured for this backend.
   * @param  exportConfig  The configuration to use when performing the export.
   *
   * @throws  DirectoryException  If a problem occurs while performing the LDIF
   *                              export.
   * {@inheritDoc}
   */
  public void exportLDIF(ConfigEntry configEntry, DN[] baseDNs,
                         LDIFExportConfig exportConfig)
@@ -1091,17 +1073,7 @@
  /**
   * Imports information from an LDIF file into this backend.  This method
   * should only be called if <CODE>supportsLDIFImport</CODE> returns
   * <CODE>true</CODE>.  Note that the server will not explicitly initialize
   * this backend before calling this method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  baseDNs       The set of base DNs configured for this backend.
   * @param  importConfig  The configuration to use when performing the import.
   *
   * @throws  DirectoryException  If a problem occurs while performing the LDIF
   *                              import.
   * {@inheritDoc}
   */
  public void importLDIF(ConfigEntry configEntry, DN[] baseDNs,
                         LDIFImportConfig importConfig)
@@ -1160,17 +1132,7 @@
  /**
   * Creates a backup of the contents of this backend in a form that may be
   * restored at a later date if necessary.  This method should only be called
   * if <CODE>supportsBackup</CODE> returns <CODE>true</CODE>.  Note that the
   * server will not explicitly initialize this backend before calling this
   * method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  backupConfig  The configuration to use when performing the backup.
   *
   * @throws  DirectoryException  If a problem occurs while performing the
   *                              backup.
   * {@inheritDoc}
   */
  public void createBackup(ConfigEntry configEntry, BackupConfig backupConfig)
         throws DirectoryException
@@ -1224,17 +1186,7 @@
  /**
   * Restores a backup of the contents of this backend.  This method should only
   * be called if <CODE>supportsRestore</CODE> returns <CODE>true</CODE>.  Note
   * that the server will not explicitly initialize this backend before calling
   * this method.
   *
   * @param  configEntry    The configuration entry for this backend.
   * @param  restoreConfig  The configuration to use when performing the
   *                        restore.
   *
   * @throws  DirectoryException  If a problem occurs while performing the
   *                              restore.
   * {@inheritDoc}
   */
  public void restoreBackup(ConfigEntry configEntry,
                            RestoreConfig restoreConfig)
@@ -1250,40 +1202,11 @@
  /**
   * Retrieves the DN of the configuration entry with which this component is
   * associated.
   *
   * @return  The DN of the configuration entry with which this component is
   *          associated.
   */
  public DN getConfigurableComponentEntryDN()
  {
    return configEntryDN;
  }
  /**
   * Retrieves the set of configuration attributes that are associated with this
   * configurable component.
   *
   * @return  The set of configuration attributes that are associated with this
   *          configurable component.
   */
  public List<ConfigAttribute> getConfigurationAttributes()
  {
    // There are no configurable attributes that will be explicitly advertised.
    return new LinkedList<ConfigAttribute>();
  }
  /**
   * Indicates whether the provided configuration entry has an acceptable
   * configuration for this component.  If it does not, then detailed
   * information about the problem(s) should be added to the provided list.
   *
   * @param  configEntry          The configuration entry for which to make the
   * @param  backendCfg          The configuration entry for which to make the
   *                              determination.
   * @param  unacceptableReasons  A list that can be used to hold messages about
   *                              why the provided entry does not have an
@@ -1292,8 +1215,9 @@
   * @return  <CODE>true</CODE> if the provided entry has an acceptable
   *          configuration for this component, or <CODE>false</CODE> if not.
   */
  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
                                            List<String> unacceptableReasons)
  public boolean isConfigurationChangeAcceptable(
       BackendCfg backendCfg,
       List<String> unacceptableReasons)
  {
    // We'll pretty much accept anything here as long as it isn't one of our
    // private attributes.
@@ -1303,23 +1227,9 @@
  /**
   * Makes a best-effort attempt to apply the configuration contained in the
   * provided entry.  Information about the result of this processing should be
   * added to the provided message list.  Information should always be added to
   * this list if a configuration change could not be applied.  If detailed
   * results are requested, then information about the changes applied
   * successfully (and optionally about parameters that were not changed) should
   * also be included.
   *
   * @param  configEntry      The entry containing the new configuration to
   *                          apply for this component.
   * @param  detailedResults  Indicates whether detailed information about the
   *                          processing should be added to the list.
   *
   * @return  Information about the result of the configuration update.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
                                                  boolean detailedResults)
  public ConfigChangeResult applyConfigurationChange(BackendCfg backendCfg)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
@@ -1328,39 +1238,54 @@
    // Check to see if there is a new set of user-defined attributes.
    ArrayList<Attribute> userAttrs = new ArrayList<Attribute>();
    for (List<Attribute> attrs :
         configEntry.getEntry().getUserAttributes().values())
    try
    {
      for (Attribute a : attrs)
      ConfigEntry configEntry = DirectoryServer.getConfigEntry(configEntryDN);
      for (List<Attribute> attrs :
           configEntry.getEntry().getUserAttributes().values())
      {
        if (! isMonitorConfigAttribute(a))
        for (Attribute a : attrs)
        {
          userAttrs.add(a);
          if (! isMonitorConfigAttribute(a))
          {
            userAttrs.add(a);
          }
        }
      }
      for (List<Attribute> attrs :
           configEntry.getEntry().getOperationalAttributes().values())
      {
        for (Attribute a : attrs)
        {
          if (! isMonitorConfigAttribute(a))
          {
            userAttrs.add(a);
          }
        }
      }
    }
    for (List<Attribute> attrs :
         configEntry.getEntry().getOperationalAttributes().values())
    catch (Exception e)
    {
      for (Attribute a : attrs)
      if (debugEnabled())
      {
        if (! isMonitorConfigAttribute(a))
        {
          userAttrs.add(a);
        }
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
    }
    userDefinedAttributes = userAttrs;
    if (detailedResults)
    {
      int    msgID   = MSGID_MONITOR_USING_NEW_USER_ATTRS;
      String message = getMessage(msgID);
      messages.add(message);
    }
    int    msgID   = MSGID_MONITOR_USING_NEW_USER_ATTRS;
    String message = getMessage(msgID);
    messages.add(message);
    currentConfig = backendCfg;
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
}
opends/src/server/org/opends/server/backends/RootDSEBackend.java
@@ -32,27 +32,22 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.server.api.Backend;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.BackendConfigManager;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
@@ -84,9 +79,13 @@
import static org.opends.server.loggers.Error.*;
import static org.opends.server.messages.BackendMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.ConfigMessages.
     MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.admin.std.server.RootDSEBackendCfg;
import org.opends.server.admin.std.meta.RootDSEBackendCfgDefn;
import org.opends.server.admin.server.ConfigurationChangeListener;
/**
@@ -103,7 +102,7 @@
 */
public class RootDSEBackend
       extends Backend
       implements ConfigurableComponent
       implements ConfigurationChangeListener<RootDSEBackendCfg>
{
  // The set of standard "static" attributes that we will always include in the
  // root DSE entry and won't change while the server is running.
@@ -124,6 +123,9 @@
  // The set of objectclasses that will be used in the root DSE entry.
  private HashMap<ObjectClass,String> dseObjectClasses;
  // The current configuration state.
  private RootDSEBackendCfg currentConfig;
  // The DN of the configuration entry for this backend.
  private DN configEntryDN;
@@ -156,20 +158,7 @@
  /**
   * Initializes this backend based on the information in the provided
   * configuration entry.
   *
   * @param  configEntry  The configuration entry that contains the information
   *                      to use to initialize this backend.
   * @param  baseDNs      The set of base DNs that have been configured for this
   *                      backend.
   *
   * @throws  ConfigException  If an unrecoverable problem arises in the
   *                           process of performing the initialization.
   *
   * @throws  InitializationException  If a problem occurs during initialization
   *                                   that is not related to the server
   *                                   configuration.
   * {@inheritDoc}
   */
  public void initializeBackend(ConfigEntry configEntry, DN[] baseDNs)
         throws ConfigException, InitializationException
@@ -184,6 +173,7 @@
    }
    configEntryDN = configEntry.getDN();
    RootDSEBackendCfg cfg = getRootDSEBackendCfg(configEntry);
    // Get the set of user-defined attributes for the configuration entry.  Any
@@ -223,15 +213,10 @@
    // Create the set of subordinate base DNs.  If this is specified in the
    // configuration, then use that set.  Otherwise, use the set of non-private
    // backends defined in the server.
    String description = getMessage(MSGID_ROOTDSE_SUBORDINATE_BASE_DESCRIPTION);
    DNConfigAttribute subDNsStub =
         new DNConfigAttribute(ATTR_ROOT_DSE_SUBORDINATE_BASE_DN, description,
                               false, true, false);
    try
    {
      DNConfigAttribute subDNsAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(subDNsStub);
      if (subDNsAttr == null)
      Set<DN> subDNs = cfg.getSubordinateBaseDN();
      if (subDNs.isEmpty())
      {
        // This is fine -- we'll just use the set of user-defined suffixes.
        subordinateBaseDNs = null;
@@ -239,7 +224,7 @@
      else
      {
        subordinateBaseDNs = new ConcurrentHashMap<DN,Backend>();
        for (DN baseDN : subDNsAttr.activeValues())
        for (DN baseDN : subDNs)
        {
          Backend backend = DirectoryServer.getBackend(baseDN);
          if (backend == null)
@@ -271,34 +256,7 @@
    // Determine whether all root DSE attributes should be treated as user
    // attributes.
    showAllAttributes = DEFAULT_ROOTDSE_SHOW_ALL_ATTRIBUTES;
    int msgID = MSGID_ROOTDSE_DESCRIPTION_SHOW_ALL_ATTRIBUTES;
    BooleanConfigAttribute showAllStub =
         new BooleanConfigAttribute(ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute showAllAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(showAllStub);
      if (showAllAttr != null)
      {
        showAllAttributes = showAllAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_ROOTDSE_CANNOT_DETERMINE_ALL_USER_ATTRIBUTES;
      String message = getMessage(msgID, ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
                                  stackTraceToSingleLineString(e));
      logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_ERROR, message,
               msgID);
    }
    showAllAttributes = cfg.isShowAllAttributes();
    // Construct the set of "static" attributes that will always be present in
@@ -344,8 +302,9 @@
    setBackendID("rootdse");
    // Register with the Directory Server as a configurable component.
    DirectoryServer.registerConfigurableComponent(this);
    // Register as a change listener.
    currentConfig = cfg;
    cfg.addChangeListener(this);
  }
@@ -363,7 +322,7 @@
   */
  public void finalizeBackend()
  {
    DirectoryServer.deregisterConfigurableComponent(this);
    currentConfig.removeChangeListener(this);
  }
@@ -1158,17 +1117,7 @@
  /**
   * Exports the contents of this backend to LDIF.  This method should only be
   * called if <CODE>supportsLDIFExport</CODE> returns <CODE>true</CODE>.  Note
   * that the server will not explicitly initialize this backend before calling
   * this method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  baseDNs       The set of base DNs configured for this backend.
   * @param  exportConfig  The configuration to use when performing the export.
   *
   * @throws  DirectoryException  If a problem occurs while performing the LDIF
   *                              export.
   * {@inheritDoc}
   */
  public void exportLDIF(ConfigEntry configEntry, DN[] baseDNs,
                         LDIFExportConfig exportConfig)
@@ -1246,17 +1195,7 @@
  /**
   * Imports information from an LDIF file into this backend.  This method
   * should only be called if <CODE>supportsLDIFImport</CODE> returns
   * <CODE>true</CODE>.  Note that the server will not explicitly initialize
   * this backend before calling this method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  baseDNs       The set of base DNs configured for this backend.
   * @param  importConfig  The configuration to use when performing the import.
   *
   * @throws  DirectoryException  If a problem occurs while performing the LDIF
   *                              import.
   * {@inheritDoc}
   */
  public void importLDIF(ConfigEntry configEntry, DN[] baseDNs,
                         LDIFImportConfig importConfig)
@@ -1315,17 +1254,7 @@
  /**
   * Creates a backup of the contents of this backend in a form that may be
   * restored at a later date if necessary.  This method should only be called
   * if <CODE>supportsBackup</CODE> returns <CODE>true</CODE>.  Note that the
   * server will not explicitly initialize this backend before calling this
   * method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  backupConfig  The configuration to use when performing the backup.
   *
   * @throws  DirectoryException  If a problem occurs while performing the
   *                              backup.
   * {@inheritDoc}
   */
  public void createBackup(ConfigEntry configEntry, BackupConfig backupConfig)
         throws DirectoryException
@@ -1379,17 +1308,7 @@
  /**
   * Restores a backup of the contents of this backend.  This method should only
   * be called if <CODE>supportsRestore</CODE> returns <CODE>true</CODE>.  Note
   * that the server will not explicitly initialize this backend before calling
   * this method.
   *
   * @param  configEntry    The configuration entry for this backend.
   * @param  restoreConfig  The configuration to use when performing the
   *                        restore.
   *
   * @throws  DirectoryException  If a problem occurs while performing the
   *                              restore.
   * {@inheritDoc}
   */
  public void restoreBackup(ConfigEntry configEntry,
                            RestoreConfig restoreConfig)
@@ -1405,88 +1324,25 @@
  /**
   * Retrieves the DN of the configuration entry with which this component is
   * associated.
   *
   * @return  The DN of the configuration entry with which this component is
   *          associated.
   * {@inheritDoc}
   */
  public DN getConfigurableComponentEntryDN()
  {
    return configEntryDN;
  }
  /**
   * Retrieves the set of configuration attributes that are associated with this
   * configurable component.
   *
   * @return  The set of configuration attributes that are associated with this
   *          configurable component.
   */
  public List<ConfigAttribute> getConfigurationAttributes()
  {
    LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
    String description = getMessage(MSGID_ROOTDSE_SUBORDINATE_BASE_DESCRIPTION);
    ArrayList<DN> values = new ArrayList<DN>();
    if (subordinateBaseDNs != null)
    {
      values.addAll(subordinateBaseDNs.keySet());
    }
    attrList.add(new DNConfigAttribute(ATTR_ROOT_DSE_SUBORDINATE_BASE_DN,
                                       description, false, true, false,
                                       values));
    description = getMessage(MSGID_ROOTDSE_DESCRIPTION_SHOW_ALL_ATTRIBUTES);
    attrList.add(new BooleanConfigAttribute(ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
                                            description, showAllAttributes));
    return attrList;
  }
  /**
   * Indicates whether the provided configuration entry has an acceptable
   * configuration for this component.  If it does not, then detailed
   * information about the problem(s) should be added to the provided list.
   *
   * @param  configEntry          The configuration entry for which to make the
   *                              determination.
   * @param  unacceptableReasons  A list that can be used to hold messages about
   *                              why the provided entry does not have an
   *                              acceptable configuration.
   *
   * @return  <CODE>true</CODE> if the provided entry has an acceptable
   *          configuration for this component, or <CODE>false</CODE> if not.
   */
  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
                                            List<String> unacceptableReasons)
  public boolean isConfigurationChangeAcceptable(
       RootDSEBackendCfg cfg,
       List<String> unacceptableReasons)
  {
    boolean configIsAcceptable = true;
    String description = getMessage(MSGID_ROOTDSE_SUBORDINATE_BASE_DESCRIPTION);
    DNConfigAttribute subDNsStub =
         new DNConfigAttribute(ATTR_ROOT_DSE_SUBORDINATE_BASE_DN, description,
                               false, true, false);
    try
    {
      DNConfigAttribute subDNsAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(subDNsStub);
      if (subDNsAttr == null)
      Set<DN> subDNs = cfg.getSubordinateBaseDN();
      if (subDNs.isEmpty())
      {
        // This is fine -- we'll just use the set of user-defined suffixes.
      }
      else
      {
        for (DN baseDN : subDNsAttr.activeValues())
        for (DN baseDN : subDNs)
        {
          Backend backend = DirectoryServer.getBackend(baseDN);
          if (backend == null)
@@ -1513,54 +1369,15 @@
    }
    description = getMessage(MSGID_ROOTDSE_DESCRIPTION_SHOW_ALL_ATTRIBUTES);
    BooleanConfigAttribute showAllStub =
         new BooleanConfigAttribute(ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
                                    description, false);
    try
    {
      BooleanConfigAttribute showAllAttr =
           (BooleanConfigAttribute) configEntry.getConfigAttribute(showAllStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int    msgID   = MSGID_ROOTDSE_CANNOT_DETERMINE_ALL_USER_ATTRIBUTES;
      String message = getMessage(msgID, ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
                                  stackTraceToSingleLineString(e));
      unacceptableReasons.add(message);
      configIsAcceptable = false;
    }
    return configIsAcceptable;
  }
  /**
   * Makes a best-effort attempt to apply the configuration contained in the
   * provided entry.  Information about the result of this processing should be
   * added to the provided message list.  Information should always be added to
   * this list if a configuration change could not be applied.  If detailed
   * results are requested, then information about the changes applied
   * successfully (and optionally about parameters that were not changed) should
   * also be included.
   *
   * @param  configEntry      The entry containing the new configuration to
   *                          apply for this component.
   * @param  detailedResults  Indicates whether detailed information about the
   *                          processing should be added to the list.
   *
   * @return  Information about the result of the configuration update.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
                                                  boolean detailedResults)
  public ConfigChangeResult applyConfigurationChange(RootDSEBackendCfg cfg)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
@@ -1569,15 +1386,10 @@
    // Check to see if we should apply a new set of base DNs.
    ConcurrentHashMap<DN,Backend> subBases;
    String description = getMessage(MSGID_ROOTDSE_SUBORDINATE_BASE_DESCRIPTION);
    DNConfigAttribute subDNsStub =
         new DNConfigAttribute(ATTR_ROOT_DSE_SUBORDINATE_BASE_DN, description,
                               false, true, false);
    try
    {
      DNConfigAttribute subDNsAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(subDNsStub);
      if (subDNsAttr == null)
      Set<DN> subDNs = cfg.getSubordinateBaseDN();
      if (subDNs.isEmpty())
      {
        // This is fine -- we'll just use the set of user-defined suffixes.
        subBases = null;
@@ -1585,7 +1397,7 @@
      else
      {
        subBases = new ConcurrentHashMap<DN,Backend>();
        for (DN baseDN : subDNsAttr.activeValues())
        for (DN baseDN : subDNs)
        {
          Backend backend = DirectoryServer.getBackend(baseDN);
          if (backend == null)
@@ -1627,62 +1439,49 @@
    }
    boolean newShowAll = DEFAULT_ROOTDSE_SHOW_ALL_ATTRIBUTES;
    description = getMessage(MSGID_ROOTDSE_DESCRIPTION_SHOW_ALL_ATTRIBUTES);
    BooleanConfigAttribute showAllStub =
         new BooleanConfigAttribute(ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
                                    description, false);
    boolean newShowAll = cfg.isShowAllAttributes();
    // Check to see if there is a new set of user-defined attributes.
    ArrayList<Attribute> userAttrs = new ArrayList<Attribute>();
    try
    {
      BooleanConfigAttribute showAllAttr =
           (BooleanConfigAttribute) configEntry.getConfigAttribute(showAllStub);
      if (showAllAttr != null)
      ConfigEntry configEntry = DirectoryServer.getConfigEntry(configEntryDN);
      for (List<Attribute> attrs :
           configEntry.getEntry().getUserAttributes().values())
      {
        newShowAll = showAllAttr.pendingValue();
        for (Attribute a : attrs)
        {
          if (! isDSEConfigAttribute(a))
          {
            userAttrs.add(a);
          }
        }
      }
      for (List<Attribute> attrs :
           configEntry.getEntry().getOperationalAttributes().values())
      {
        for (Attribute a : attrs)
        {
          if (! isDSEConfigAttribute(a))
          {
            userAttrs.add(a);
          }
        }
      }
    }
    catch (Exception e)
    catch (ConfigException e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_ROOTDSE_CANNOT_DETERMINE_ALL_USER_ATTRIBUTES;
      String message = getMessage(msgID, ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
                                  stackTraceToSingleLineString(e));
      messages.add(message);
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    // Check to see if there is a new set of user-defined attributes.
    ArrayList<Attribute> userAttrs = new ArrayList<Attribute>();
    for (List<Attribute> attrs :
         configEntry.getEntry().getUserAttributes().values())
    {
      for (Attribute a : attrs)
      {
        if (! isDSEConfigAttribute(a))
        {
          userAttrs.add(a);
        }
      }
    }
    for (List<Attribute> attrs :
         configEntry.getEntry().getOperationalAttributes().values())
    {
      for (Attribute a : attrs)
      {
        if (! isDSEConfigAttribute(a))
        {
          userAttrs.add(a);
        }
      }
      int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
    }
@@ -1690,65 +1489,63 @@
    {
      subordinateBaseDNs = subBases;
      if (detailedResults)
      if (subordinateBaseDNs == null)
      {
        if (subordinateBaseDNs == null)
        int msgID = MSGID_ROOTDSE_USING_SUFFIXES_AS_BASE_DNS;
        String message = getMessage(msgID);
        messages.add(message);
      }
      else
      {
        StringBuilder basesStr = new StringBuilder();
        for (DN dn : subordinateBaseDNs.keySet())
        {
          int msgID = MSGID_ROOTDSE_USING_SUFFIXES_AS_BASE_DNS;
          String message = getMessage(msgID);
          messages.add(message);
        }
        else
        {
          StringBuilder basesStr = new StringBuilder();
          Iterator<DN> iterator = subordinateBaseDNs.keySet().iterator();
          while (iterator.hasNext())
          if (basesStr.length() > 0)
          {
            if (basesStr.length() > 0)
            {
              basesStr.append(", ");
            }
            else
            {
              basesStr.append("{ ");
            }
            basesStr.append(iterator.next());
            basesStr.append(", ");
          }
          else
          {
            basesStr.append("{ ");
          }
          basesStr.append(" }");
          int msgID = MSGID_ROOTDSE_USING_NEW_SUBORDINATE_BASE_DNS;
          String message = getMessage(msgID, basesStr.toString());
          messages.add(message);
          basesStr.append(dn);
        }
        basesStr.append(" }");
        int msgID = MSGID_ROOTDSE_USING_NEW_SUBORDINATE_BASE_DNS;
        String message = getMessage(msgID, basesStr.toString());
        messages.add(message);
      }
      if (showAllAttributes != newShowAll)
      {
        showAllAttributes = newShowAll;
        if (detailedResults)
        {
          int    msgID   = MSGID_ROOTDSE_UPDATED_SHOW_ALL_ATTRS;
          String message = getMessage(msgID, ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
                                      showAllAttributes);
          messages.add(message);
        }
        int    msgID   = MSGID_ROOTDSE_UPDATED_SHOW_ALL_ATTRS;
        String message = getMessage(msgID, ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
                                    showAllAttributes);
        messages.add(message);
      }
      userDefinedAttributes = userAttrs;
      if (detailedResults)
      {
        int    msgID   = MSGID_ROOTDSE_USING_NEW_USER_ATTRS;
        String message = getMessage(msgID);
        messages.add(message);
      }
      int    msgID   = MSGID_ROOTDSE_USING_NEW_USER_ATTRS;
      String message = getMessage(msgID);
      messages.add(message);
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  private static RootDSEBackendCfg getRootDSEBackendCfg(ConfigEntry configEntry)
      throws ConfigException {
    return BackendConfigManager.getConfiguration(
         RootDSEBackendCfgDefn.getInstance(), configEntry);
  }
}
opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -59,13 +59,9 @@
import org.opends.server.api.AlertGenerator;
import org.opends.server.api.Backend;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.api.MatchingRule;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
@@ -73,6 +69,7 @@
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SchemaConfigManager;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.BackendConfigManager;
import org.opends.server.schema.AttributeTypeSyntax;
import org.opends.server.schema.DITContentRuleSyntax;
import org.opends.server.schema.DITStructureRuleSyntax;
@@ -125,7 +122,10 @@
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.admin.std.server.SchemaBackendCfg;
import org.opends.server.admin.std.meta.SchemaBackendCfgDefn;
import org.opends.server.admin.server.ConfigurationChangeListener;
import static org.opends.server.messages.ConfigMessages.*;
/**
@@ -134,8 +134,8 @@
 * rather dynamically generates the schema entry whenever it is requested.
 */
public class SchemaBackend
       extends Backend
       implements ConfigurableComponent, AlertGenerator
     extends Backend
     implements ConfigurationChangeListener<SchemaBackendCfg>, AlertGenerator
{
  /**
   * The fully-qualified name of this class.
@@ -213,6 +213,9 @@
  // The DN of the configuration entry for this backend.
  private DN configEntryDN;
  // The current configuration state.
  private SchemaBackendCfg currentConfig;
  // The set of base DNs for this backend.
  private DN[] baseDNs;
@@ -245,20 +248,7 @@
  /**
   * Initializes this backend based on the information in the provided
   * configuration entry.
   *
   * @param  configEntry  The configuration entry that contains the information
   *                      to use to initialize this backend.
   * @param  baseDNs      The set of base DNs that have been configured for this
   *                      backend.
   *
   * @throws  ConfigException  If an unrecoverable problem arises in the
   *                           process of performing the initialization.
   *
   * @throws  InitializationException  If a problem occurs during initialization
   *                                   that is not related to the server
   *                                   configuration.
   * {@inheritDoc}
   */
  public void initializeBackend(ConfigEntry configEntry, DN[] baseDNs)
         throws ConfigException, InitializationException
@@ -273,7 +263,7 @@
    }
    configEntryDN = configEntry.getDN();
    SchemaBackendCfg cfg = getSchemaBackendCfg(configEntry);
    // Get all of the attribute types that we will use for schema elements.
    attributeTypesType =
@@ -347,32 +337,7 @@
    // Determine whether to show all attributes.
    showAllAttributes = DEFAULT_SCHEMA_SHOW_ALL_ATTRIBUTES;
    int msgID = MSGID_SCHEMA_DESCRIPTION_SHOW_ALL_ATTRIBUTES;
    BooleanConfigAttribute showAllStub =
         new BooleanConfigAttribute(ATTR_SCHEMA_SHOW_ALL_ATTRIBUTES,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute showAllAttr =
           (BooleanConfigAttribute) configEntry.getConfigAttribute(showAllStub);
      if (showAllAttr != null)
      {
        showAllAttributes = showAllAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SCHEMA_CANNOT_DETERMINE_SHOW_ALL;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new ConfigException(msgID, message, e);
    }
    showAllAttributes = cfg.isShowAllAttributes();
    // Register each of the suffixes with the Directory Server.  Also, register
@@ -392,7 +357,7 @@
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
        int msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
        String message = getMessage(msgID, baseDNs[i].toString(),
                                    stackTraceToSingleLineString(e));
        throw new InitializationException(msgID, message, e);
@@ -469,7 +434,7 @@
          }
          else
          {
            msgID = MSGID_SCHEMA_CANNOT_FIND_CONCAT_FILE;
            int msgID = MSGID_SCHEMA_CANNOT_FIND_CONCAT_FILE;
            String message = getMessage(msgID,
                                        upgradeDirectory.getAbsolutePath(),
                                        SCHEMA_CONCAT_FILE_NAME,
@@ -521,7 +486,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SCHEMA_ERROR_DETERMINING_SCHEMA_CHANGES;
      int msgID = MSGID_SCHEMA_ERROR_DETERMINING_SCHEMA_CHANGES;
      String message = getMessage(msgID, stackTraceToSingleLineString(e));
      logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR, message,
               msgID);
@@ -529,7 +494,8 @@
    // Register with the Directory Server as a configurable component.
    DirectoryServer.registerConfigurableComponent(this);
    currentConfig = cfg;
    cfg.addSchemaChangeListener(this);
  }
@@ -547,7 +513,7 @@
   */
  public void finalizeBackend()
  {
    DirectoryServer.deregisterConfigurableComponent(this);
    currentConfig.removeSchemaChangeListener(this);
    for (DN baseDN : baseDNs)
    {
@@ -4169,17 +4135,7 @@
  /**
   * Exports the contents of this backend to LDIF.  This method should only be
   * called if <CODE>supportsLDIFExport</CODE> returns <CODE>true</CODE>.  Note
   * that the server will not explicitly initialize this backend before calling
   * this method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  baseDNs       The set of base DNs configured for this backend.
   * @param  exportConfig  The configuration to use when performing the export.
   *
   * @throws  DirectoryException  If a problem occurs while performing the LDIF
   *                              export.
   * {@inheritDoc}
   */
  public void exportLDIF(ConfigEntry configEntry, DN[] baseDNs,
                         LDIFExportConfig exportConfig)
@@ -4258,17 +4214,7 @@
  /**
   * Imports information from an LDIF file into this backend.  This method
   * should only be called if <CODE>supportsLDIFImport</CODE> returns
   * <CODE>true</CODE>.  Note that the server will not explicitly initialize
   * this backend before calling this method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  baseDNs       The set of base DNs configured for this backend.
   * @param  importConfig  The configuration to use when performing the import.
   *
   * @throws  DirectoryException  If a problem occurs while performing the LDIF
   *                              import.
   * {@inheritDoc}
   */
  public void importLDIF(ConfigEntry configEntry, DN[] baseDNs,
                         LDIFImportConfig importConfig)
@@ -4330,17 +4276,7 @@
  /**
   * Creates a backup of the contents of this backend in a form that may be
   * restored at a later date if necessary.  This method should only be called
   * if <CODE>supportsBackup</CODE> returns <CODE>true</CODE>.  Note that the
   * server will not explicitly initialize this backend before calling this
   * method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  backupConfig  The configuration to use when performing the backup.
   *
   * @throws  DirectoryException  If a problem occurs while performing the
   *                              backup.
   * {@inheritDoc}
   */
  public void createBackup(ConfigEntry configEntry, BackupConfig backupConfig)
         throws DirectoryException
@@ -4737,17 +4673,7 @@
  /**
   * Restores a backup of the contents of this backend.  This method should only
   * be called if <CODE>supportsRestore</CODE> returns <CODE>true</CODE>.  Note
   * that the server will not explicitly initialize this backend before calling
   * this method.
   *
   * @param  configEntry    The configuration entry for this backend.
   * @param  restoreConfig  The configuration to use when performing the
   *                        restore.
   *
   * @throws  DirectoryException  If a problem occurs while performing the
   *                              restore.
   * {@inheritDoc}
   */
  public void restoreBackup(ConfigEntry configEntry,
                            RestoreConfig restoreConfig)
@@ -5253,82 +5179,37 @@
  /**
   * Retrieves the DN of the configuration entry with which this component is
   * associated.
   *
   * @return  The DN of the configuration entry with which this component is
   *          associated.
   * {@inheritDoc}
   */
  public DN getConfigurableComponentEntryDN()
  public boolean isConfigurationChangeAcceptable(
       SchemaBackendCfg configEntry,
       List<String> unacceptableReasons)
  {
    return configEntryDN;
    return true;
  }
  /**
   * Retrieves the set of configuration attributes that are associated with this
   * configurable component.
   *
   * @return  The set of configuration attributes that are associated with this
   *          configurable component.
   * {@inheritDoc}
   */
  public List<ConfigAttribute> getConfigurationAttributes()
  public ConfigChangeResult applyConfigurationChange(
       SchemaBackendCfg backendCfg)
  {
    LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
    String description = getMessage(MSGID_SCHEMA_DESCRIPTION_ENTRY_DN);
    ArrayList<DN> values = new ArrayList<DN>(baseDNs.length);
    for (DN baseDN : baseDNs)
    {
      values.add(baseDN);
    }
    attrList.add(new DNConfigAttribute(ATTR_SCHEMA_ENTRY_DN, description,
                                       false, true, false, values));
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    description = getMessage(MSGID_SCHEMA_DESCRIPTION_SHOW_ALL_ATTRIBUTES);
    attrList.add(new BooleanConfigAttribute(ATTR_SCHEMA_SHOW_ALL_ATTRIBUTES,
                                            description, false,
                                            showAllAttributes));
    return attrList;
  }
  /**
   * Indicates whether the provided configuration entry has an acceptable
   * configuration for this component.  If it does not, then detailed
   * information about the problem(s) should be added to the provided list.
   *
   * @param  configEntry          The configuration entry for which to make the
   *                              determination.
   * @param  unacceptableReasons  A list that can be used to hold messages about
   *                              why the provided entry does not have an
   *                              acceptable configuration.
   *
   * @return  <CODE>true</CODE> if the provided entry has an acceptable
   *          configuration for this component, or <CODE>false</CODE> if not.
   */
  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
                                            List<String> unacceptableReasons)
  {
    boolean configIsAcceptable = true;
    String description = getMessage(MSGID_SCHEMA_DESCRIPTION_ENTRY_DN);
    DNConfigAttribute baseDNStub =
         new DNConfigAttribute(ATTR_SCHEMA_ENTRY_DN, description, false, true,
                               false);
    // Check to see if we should apply a new set of base DNs.
    Set<DN> newBaseDNs;
    try
    {
      // We don't care what the DNs are as long as we can parse them.
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      newBaseDNs = backendCfg.getSchemaEntryDN();
      if (newBaseDNs.isEmpty())
      {
        newBaseDNs.add(DN.decode(DN_DEFAULT_SCHEMA_ROOT));
      }
    }
    catch (Exception e)
    {
@@ -5338,104 +5219,6 @@
      }
      int msgID = MSGID_SCHEMA_CANNOT_DETERMINE_BASE_DN;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      unacceptableReasons.add(message);
      configIsAcceptable = false;
    }
    description = getMessage(MSGID_SCHEMA_DESCRIPTION_SHOW_ALL_ATTRIBUTES);
    BooleanConfigAttribute showAllStub =
         new BooleanConfigAttribute(ATTR_SCHEMA_SHOW_ALL_ATTRIBUTES,
                                    description, false);
    try
    {
      // We don't care what the value is as long as we can parse it.
      BooleanConfigAttribute showAllAttr =
           (BooleanConfigAttribute) configEntry.getConfigAttribute(showAllStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_SCHEMA_CANNOT_DETERMINE_SHOW_ALL;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      unacceptableReasons.add(message);
      configIsAcceptable = false;
    }
    return configIsAcceptable;
  }
  /**
   * Makes a best-effort attempt to apply the configuration contained in the
   * provided entry.  Information about the result of this processing should be
   * added to the provided message list.  Information should always be added to
   * this list if a configuration change could not be applied.  If detailed
   * results are requested, then information about the changes applied
   * successfully (and optionally about parameters that were not changed) should
   * also be included.
   *
   * @param  configEntry      The entry containing the new configuration to
   *                          apply for this component.
   * @param  detailedResults  Indicates whether detailed information about the
   *                          processing should be added to the list.
   *
   * @return  Information about the result of the configuration update.
   */
  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
                                                  boolean detailedResults)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    // Check to see if we should apply a new set of base DNs.
    HashSet<DN> newBaseDNs;
    int msgID = MSGID_SCHEMA_DESCRIPTION_ENTRY_DN;
    DNConfigAttribute baseDNStub =
         new DNConfigAttribute(ATTR_SCHEMA_ENTRY_DN, getMessage(msgID), false,
                               true, false);
    try
    {
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      if (baseDNAttr == null)
      {
        newBaseDNs = new HashSet<DN>(1);
        newBaseDNs.add(DN.decode(DN_DEFAULT_SCHEMA_ROOT));
      }
      else
      {
        List<DN> newDNList = baseDNAttr.activeValues();
        if ((newDNList == null) || newDNList.isEmpty())
        {
          newBaseDNs = new HashSet<DN>(1);
          newBaseDNs.add(DN.decode(DN_DEFAULT_SCHEMA_ROOT));
        }
        else
        {
          newBaseDNs = new HashSet<DN>(newDNList);
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SCHEMA_CANNOT_DETERMINE_BASE_DN;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
@@ -5445,58 +5228,48 @@
    // Check to see if we should change the behavior regarding whether to show
    // all schema attributes.
    boolean newShowAllAttributes = DEFAULT_SCHEMA_SHOW_ALL_ATTRIBUTES;
    msgID = MSGID_SCHEMA_DESCRIPTION_SHOW_ALL_ATTRIBUTES;
    BooleanConfigAttribute showAllStub =
         new BooleanConfigAttribute(ATTR_SCHEMA_SHOW_ALL_ATTRIBUTES,
                                    getMessage(msgID), false);
    boolean newShowAllAttributes = backendCfg.isShowAllAttributes();
    // Check to see if there is a new set of user-defined attributes.
    ArrayList<Attribute> newUserAttrs = new ArrayList<Attribute>();
    try
    {
      BooleanConfigAttribute showAllAttr =
           (BooleanConfigAttribute) configEntry.getConfigAttribute(showAllStub);
      if (showAllAttr != null)
      ConfigEntry configEntry = DirectoryServer.getConfigEntry(configEntryDN);
      for (List<Attribute> attrs :
           configEntry.getEntry().getUserAttributes().values())
      {
        newShowAllAttributes = showAllAttr.activeValue();
        for (Attribute a : attrs)
        {
          if (! isSchemaConfigAttribute(a))
          {
            newUserAttrs.add(a);
          }
        }
      }
      for (List<Attribute> attrs :
           configEntry.getEntry().getOperationalAttributes().values())
      {
        for (Attribute a : attrs)
        {
          if (! isSchemaConfigAttribute(a))
          {
            newUserAttrs.add(a);
          }
        }
      }
    }
    catch (Exception e)
    catch (ConfigException e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SCHEMA_CANNOT_DETERMINE_SHOW_ALL;
      int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      newBaseDNs = null;
    }
    // Check to see if there is a new set of user-defined attributes.
    ArrayList<Attribute> newUserAttrs = new ArrayList<Attribute>();
    for (List<Attribute> attrs :
         configEntry.getEntry().getUserAttributes().values())
    {
      for (Attribute a : attrs)
      {
        if (! isSchemaConfigAttribute(a))
        {
          newUserAttrs.add(a);
        }
      }
    }
    for (List<Attribute> attrs :
         configEntry.getEntry().getOperationalAttributes().values())
    {
      for (Attribute a : attrs)
      {
        if (! isSchemaConfigAttribute(a))
        {
          newUserAttrs.add(a);
        }
      }
    }
@@ -5525,11 +5298,8 @@
        try
        {
          DirectoryServer.deregisterBaseDN(dn, false);
          if (detailedResults)
          {
            msgID = MSGID_SCHEMA_DEREGISTERED_BASE_DN;
            messages.add(getMessage(msgID, String.valueOf(dn)));
          }
          int msgID = MSGID_SCHEMA_DEREGISTERED_BASE_DN;
          messages.add(getMessage(msgID, String.valueOf(dn)));
        }
        catch (Exception e)
        {
@@ -5538,7 +5308,7 @@
            debugCaught(DebugLogLevel.ERROR, e);
          }
          msgID = MSGID_SCHEMA_CANNOT_DEREGISTER_BASE_DN;
          int msgID = MSGID_SCHEMA_CANNOT_DEREGISTER_BASE_DN;
          messages.add(getMessage(msgID, String.valueOf(dn),
                                  stackTraceToSingleLineString(e)));
          resultCode = DirectoryServer.getServerErrorResultCode();
@@ -5551,11 +5321,8 @@
        try
        {
          DirectoryServer.registerBaseDN(dn, this, true, false);
          if (detailedResults)
          {
            msgID = MSGID_SCHEMA_REGISTERED_BASE_DN;
            messages.add(getMessage(msgID, String.valueOf(dn)));
          }
          int msgID = MSGID_SCHEMA_REGISTERED_BASE_DN;
          messages.add(getMessage(msgID, String.valueOf(dn)));
        }
        catch (Exception e)
        {
@@ -5564,7 +5331,7 @@
            debugCaught(DebugLogLevel.ERROR, e);
          }
          msgID = MSGID_SCHEMA_CANNOT_REGISTER_BASE_DN;
          int msgID = MSGID_SCHEMA_CANNOT_REGISTER_BASE_DN;
          messages.add(getMessage(msgID, String.valueOf(dn),
                                  stackTraceToSingleLineString(e)));
          resultCode = DirectoryServer.getServerErrorResultCode();
@@ -5576,15 +5343,13 @@
      userDefinedAttributes = newUserAttrs;
      if (detailedResults)
      {
        msgID = MSGID_SCHEMA_USING_NEW_USER_ATTRS;
        String message = getMessage(msgID);
        messages.add(message);
      }
      int msgID = MSGID_SCHEMA_USING_NEW_USER_ATTRS;
      String message = getMessage(msgID);
      messages.add(message);
    }
    currentConfig = backendCfg;
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
@@ -5668,5 +5433,13 @@
    return alerts;
  }
  private static SchemaBackendCfg getSchemaBackendCfg(ConfigEntry configEntry)
      throws ConfigException {
    return BackendConfigManager.getConfiguration(
         SchemaBackendCfgDefn.getInstance(), configEntry);
  }
}
opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
@@ -35,63 +35,47 @@
import com.sleepycat.je.DatabaseException;
import org.opends.server.api.Backend;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.api.MonitorProvider;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.types.BackupConfig;
import org.opends.server.types.BackupDirectory;
import org.opends.server.types.CancelledOperationException;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.RestoreConfig;
import org.opends.server.types.ResultCode;
import org.opends.server.core.BackendConfigManager;
import org.opends.server.util.LDIFException;
import org.opends.server.util.Validator;
import static org.opends.server.messages.BackendMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.messages.JebMessages.*;
import static org.opends.server.loggers.Error.logError;
import static org.opends.server.messages.ConfigMessages.*;
import static org.opends.server.config.ConfigConstants.ATTR_BACKEND_BASE_DN;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.*;
import static org.opends.server.util.ServerConstants.OID_SUBTREE_DELETE_CONTROL;
import static org.opends.server.util.ServerConstants.OID_PAGED_RESULTS_CONTROL;
import static org.opends.server.util.ServerConstants.OID_MANAGE_DSAIT_CONTROL;
import org.opends.server.admin.std.server.JEBackendCfg;
import org.opends.server.admin.std.meta.JEBackendCfgDefn;
import org.opends.server.admin.server.ConfigurationChangeListener;
/**
 * This is an implementation of a Directory Server Backend which stores entries
 * locally in a Sleepycat JE database.
 */
public class BackendImpl extends Backend implements ConfigurableComponent
public class BackendImpl
     extends Backend
     implements ConfigurationChangeListener<JEBackendCfg>
{
  /**
   * The DN of the configuration entry for this backend.
   */
  private DN configDN;
  /**
   * The configuration of this JE backend.
   */
  private Config config;
  private JEBackendCfg currentConfig;
  /**
   * The root JE container to use for this backend.
@@ -99,12 +83,6 @@
  private RootContainer rootContainer;
  /**
   * A configurable component to handle changes to the configuration of
   * the database environment.
   */
  private ConfigurableEnvironment configurableEnv = null;
  /**
   * A count of the total operation threads currently in the backend.
   */
  private AtomicInteger threadTotalCount = new AtomicInteger(0);
@@ -125,17 +103,6 @@
   */
  private static HashSet<String> supportedControls;
  /**
   * The set of configuration attributes associated with the backend in its role
   * as a configurable component.
   */
  private static ArrayList<ConfigAttribute> configAttrs;
  /**
   * The configuration attribute stub for the backendBaseDN attribute.
   */
  private static DNConfigAttribute baseDNStub;
  static
@@ -145,17 +112,6 @@
    supportedControls.add(OID_SUBTREE_DELETE_CONTROL);
    supportedControls.add(OID_PAGED_RESULTS_CONTROL);
    supportedControls.add(OID_MANAGE_DSAIT_CONTROL);
    configAttrs = new ArrayList<ConfigAttribute>();
    // ds-cfg-backendBaseDN configuration attribute.
    int msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
    baseDNStub = new DNConfigAttribute(ATTR_BACKEND_BASE_DN,
                                       getMessage(msgID),
                                       true, true, true);
    configAttrs.add(baseDNStub);
  }
@@ -256,29 +212,17 @@
  /**
   * Initializes this backend based on the information in the provided
   * configuration entry.
   *
   * @param  configEntry  The configuration entry that contains the information
   *                      to use to initialize this backend.
   * @param  baseDNs      The set of base DNs that have been configured for this
   *                      backend.
   *
   * @throws  ConfigException  If an unrecoverable problem arises in the
   *                           process of performing the initialization.
   *
   * @throws  InitializationException  If a problem occurs during initialization
   *                                   that is not related to the server
   *                                   configuration.
   * {@inheritDoc}
   */
  public void initializeBackend(ConfigEntry configEntry, DN[] baseDNs)
       throws ConfigException, InitializationException
  {
    configDN = configEntry.getDN();
    Validator.ensureNotNull(configEntry);
    JEBackendCfg backendCfg = getJEBackendCfg(configEntry);
    // Initialize a config object
    config = new Config();
    config.initializeConfig(configEntry, baseDNs);
    config.initializeConfig(backendCfg, baseDNs);
    for (DN dn : baseDNs)
    {
@@ -370,16 +314,11 @@
                                        message, databaseException);
    }
    // Register this backend as a configurable component.
    DirectoryServer.registerConfigurableComponent(this);
    // Register the database environment as a configurable component.
    ConfigurableEnvironment configurableEnv =
        rootContainer.getConfigurableEnvironment();
    if (configurableEnv != null)
    {
      DirectoryServer.registerConfigurableComponent(configurableEnv);
    }
    // Register this backend as a change listener.
    currentConfig = backendCfg;
    backendCfg.addJEChangeListener(this);
    backendCfg.addJEChangeListener(config);
    backendCfg.addJEChangeListener(rootContainer);
  }
  /**
@@ -393,17 +332,12 @@
   */
  public void finalizeBackend()
  {
    // Deregister our configurable components.
    // TODO: configurableEnv is always null and will not be deregistered.
    if (configurableEnv != null)
    {
      DirectoryServer.deregisterConfigurableComponent(configurableEnv);
      configurableEnv = null;
    }
    DirectoryServer.deregisterConfigurableComponent(this);
    // Deregister as a change listener.
    currentConfig.removeJEChangeListener(rootContainer);
    currentConfig.removeJEChangeListener(config);
    currentConfig.removeJEChangeListener(this);
    // Deregister our suffixes.
    // FIXME: Currently assuming every base DN is also a suffix.
    // Deregister our base DNs.
    for (DN dn : rootContainer.getBaseDNs())
    {
      try
@@ -943,16 +877,7 @@
  /**
   * Exports the contents of this backend to LDIF.  This method should only be
   * called if <CODE>supportsLDIFExport</CODE> returns <CODE>true</CODE>.  Note
   * that the server will not explicitly initialize this backend before calling
   * this method.
   *
   * @param configEntry  The configuration entry for this backend.
   * @param baseDNs      The set of base DNs configured for this backend.
   * @param exportConfig The configuration to use when performing the export.
   * @throws org.opends.server.types.DirectoryException
   *          If a problem occurs while performing the LDIF export.
   * {@inheritDoc}
   */
  public void exportLDIF(ConfigEntry configEntry, DN[] baseDNs,
                         LDIFExportConfig exportConfig)
@@ -1062,16 +987,7 @@
  /**
   * Imports information from an LDIF file into this backend.  This method
   * should only be called if <CODE>supportsLDIFImport</CODE> returns
   * <CODE>true</CODE>.  Note that the server will not explicitly initialize
   * this backend before calling this method.
   *
   * @param configEntry  The configuration entry for this backend.
   * @param baseDNs      The set of base DNs configured for this backend.
   * @param importConfig The configuration to use when performing the import.
   * @throws DirectoryException If a problem occurs while performing the LDIF
   *                            import.
   * {@inheritDoc}
   */
  public void importLDIF(ConfigEntry configEntry, DN[] baseDNs,
                         LDIFImportConfig importConfig)
@@ -1239,11 +1155,14 @@
                             ConfigEntry configEntry, DN[] baseDNs)
       throws InitializationException, ConfigException, DirectoryException
  {
    JEBackendCfg backendCfg = getJEBackendCfg(configEntry);
    // If the backend already has the root container open, we must use the same
    // underlying root container
    boolean openRootContainer = rootContainer == null;
    // If the rootContainer is open, the backend is initizlied by something else
    // If the rootContainer is open, the backend is initialized by something
    // else.
    // We can't do any rebuild of system indexes while others are using this
    // backend. Throw error. TODO: Need to make baseDNs disablable.
    if(!openRootContainer && rebuildConfig.includesSystemIndex())
@@ -1259,7 +1178,7 @@
      {
        // Initialize a config object.
        config = new Config();
        config.initializeConfig(configEntry, baseDNs);
        config.initializeConfig(backendCfg, baseDNs);
        // Open the database environment
        rootContainer = new RootContainer(config, this);
@@ -1316,16 +1235,7 @@
  /**
   * Creates a backup of the contents of this backend in a form that may be
   * restored at a later date if necessary.  This method should only be called
   * if <CODE>supportsBackup</CODE> returns <CODE>true</CODE>.  Note that the
   * server will not explicitly initialize this backend before calling this
   * method.
   *
   * @param configEntry  The configuration entry for this backend.
   * @param backupConfig The configuration to use when performing the backup.
   * @throws DirectoryException If a problem occurs while performing the
   *                            backup.
   * {@inheritDoc}
   */
  public void createBackup(ConfigEntry configEntry, BackupConfig backupConfig)
       throws DirectoryException
@@ -1338,15 +1248,7 @@
  /**
   * Removes the specified backup if it is possible to do so.
   *
   * @param backupDirectory The backup directory structure with which the
   *                        specified backup is associated.
   * @param backupID        The backup ID for the backup to be removed.
   * @throws DirectoryException If it is not possible to remove the specified
   *                            backup for some reason (e.g., no such backup
   *                            exists or there are other backups that are
   *                            dependent upon it).
   * {@inheritDoc}
   */
  public void removeBackup(BackupDirectory backupDirectory, String backupID)
       throws DirectoryException
@@ -1359,15 +1261,7 @@
  /**
   * Restores a backup of the contents of this backend.  This method should only
   * be called if <CODE>supportsRestore</CODE> returns <CODE>true</CODE>.  Note
   * that the server will not explicitly initialize this backend before calling
   * this method.
   *
   * @param configEntry   The configuration entry for this backend.
   * @param restoreConfig The configuration to use when performing the restore.
   * @throws DirectoryException If a problem occurs while performing the
   *                            restore.
   * {@inheritDoc}
   */
  public void restoreBackup(ConfigEntry configEntry,
                            RestoreConfig restoreConfig)
@@ -1381,107 +1275,23 @@
  /**
   * Retrieves the DN of the configuration entry with which this component is
   * associated.
   *
   * @return The DN of the configuration entry with which this component is
   *         associated.
   * {@inheritDoc}
   */
  public DN getConfigurableComponentEntryDN()
  public boolean isConfigurationChangeAcceptable(
       JEBackendCfg cfg,
       List<String> unacceptableReasons)
  {
    return configDN;
    // This listener handles only the changes to the base DNs.
    // The base DNs are checked by the backend config manager.
    return true;
  }
  /**
   * Retrieves the set of configuration attributes that are associated with this
   * configurable component.
   *
   * @return The set of configuration attributes that are associated with this
   *         configurable component.
   * {@inheritDoc}
   */
  public List<ConfigAttribute> getConfigurationAttributes()
  {
    return configAttrs;
  }
  /**
   * Indicates whether the provided configuration entry has an acceptable
   * configuration for this component.  If it does not, then detailed
   * information about the problem(s) should be added to the provided list.
   *
   * @param configEntry         The configuration entry for which to make the
   *                            determination.
   * @param unacceptableReasons A list that can be used to hold messages about
   *                            why the provided entry does not have an
   *                            acceptable configuration.
   * @return <CODE>true</CODE> if the provided entry has an acceptable
   *         configuration for this component, or <CODE>false</CODE> if not.
   */
  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
                                            List<String> unacceptableReasons)
  {
    DN[] baseDNs = null;
    boolean acceptable = true;
    try
    {
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      if (baseDNAttr == null)
      {
        int msgID = MSGID_CONFIG_BACKEND_NO_BASE_DNS;
        String message = getMessage(msgID, String.valueOf(configEntry.getDN()));
        unacceptableReasons.add(message);
      }
      else
      {
        baseDNs = baseDNAttr.activeValues().toArray(new DN[0]);
      }
    }
    catch (ConfigException e)
    {
      unacceptableReasons.add(e.getMessage());
      acceptable = false;
    }
    Config newConfig = new Config();
    try
    {
      newConfig.initializeConfig(configEntry, baseDNs);
    }
    catch (ConfigException e)
    {
      unacceptableReasons.add(e.getMessage());
      acceptable = false;
    }
    return acceptable;
  }
  /**
   * Makes a best-effort attempt to apply the configuration contained in the
   * provided entry.  Information about the result of this processing should be
   * added to the provided message list.  Information should always be added to
   * this list if a configuration change could not be applied.  If detailed
   * results are requested, then information about the changes applied
   * successfully (and optionally about parameters that were not changed) should
   * also be included.
   *
   * @param configEntry     The entry containing the new configuration to apply
   *                        for this component.
   * @param detailedResults Indicates whether detailed information about the
   *                        processing should be added to the list.
   *
   * @return  Information about the result of the configuration update.
   */
  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
                                                  boolean detailedResults)
  public ConfigChangeResult applyConfigurationChange(JEBackendCfg cfg)
  {
    ConfigChangeResult ccr;
    ResultCode resultCode = ResultCode.SUCCESS;
@@ -1489,22 +1299,14 @@
    try
    {
      DN[] baseDNs = null;
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      baseDNs = baseDNAttr.activeValues().toArray(new DN[0]);
      // Create a new Config object.
      Config newConfig = new Config();
      newConfig.initializeConfig(configEntry, baseDNs);
      DN[] baseDNs = new DN[cfg.getBackendBaseDN().size()];
      baseDNs = cfg.getBackendBaseDN().toArray(baseDNs);
      // Check for changes to the base DNs.
      for (DN baseDN : config.getBaseDNs())
      {
        boolean found = false;
        for (DN dn : newConfig.getBaseDNs())
        for (DN dn : baseDNs)
        {
          if (dn.equals(baseDN))
          {
@@ -1523,7 +1325,7 @@
        }
      }
      for (DN baseDN : newConfig.getBaseDNs())
      for (DN baseDN : baseDNs)
      {
        if (!rootContainer.getBaseDNs().contains(baseDN))
        {
@@ -1551,11 +1353,8 @@
        }
      }
      // Apply new JE configuration
      rootContainer.applyNewConfig(config);
      // Put the new configuration in place.
      config = newConfig;
      currentConfig = cfg;
    }
    catch (Exception e)
    {
@@ -1601,4 +1400,20 @@
    config.initializeConfig(configEntry, baseDNs);
    EnvManager.removeFiles(config.getBackendDirectory().getPath());
  }
  /**
   * Gets the JE backend configuration corresponding to a JE
   * backend config entry.
   *
   * @param configEntry A JE backend config entry.
   * @return Returns the JE backend configuration.
   * @throws ConfigException If the config entry could not be decoded.
   */
  static JEBackendCfg getJEBackendCfg(ConfigEntry configEntry)
      throws ConfigException {
    return BackendConfigManager.getConfiguration(
         JEBackendCfgDefn.getInstance(), configEntry);
  }
}
opends/src/server/org/opends/server/backends/jeb/Config.java
@@ -28,55 +28,42 @@
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.messages.ConfigMessages.
     MSGID_CONFIG_DESCRIPTION_BACKEND_DIRECTORY;
import static org.opends.server.messages.ConfigMessages.
     MSGID_CONFIG_DESCRIPTION_BACKEND_MODE;
import static org.opends.server.messages.ConfigMessages.
    MSGID_CONFIG_BACKEND_MODE_INVALID;
import static org.opends.server.messages.ConfigMessages.
    MSGID_CONFIG_BACKEND_INSANE_MODE;
import static org.opends.server.config.ConfigConstants.ATTR_BACKEND_DIRECTORY;
import static org.opends.server.config.ConfigConstants.ATTR_BACKEND_MODE;
import static org.opends.server.messages.ConfigMessages.
     MSGID_CONFIG_BACKEND_NO_DIRECTORY;
import static org.opends.server.messages.JebMessages.*;
import static org.opends.server.loggers.Error.logError;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.getFileForPath;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigConstants;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.IntegerConfigAttribute;
import org.opends.server.config.IntegerWithUnitConfigAttribute;
import org.opends.server.config.MultiChoiceConfigAttribute;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.RDN;
import org.opends.server.types.FilePermission;
import com.sleepycat.je.EnvironmentConfig;
import java.util.HashMap;
import java.util.Map;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.io.File;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.*;
import org.opends.server.admin.std.server.JEBackendCfg;
import org.opends.server.admin.server.ConfigurationChangeListener;
/**
 * This class represents the configuration of a JE backend.
 */
public class Config
     implements ConfigurationChangeListener<JEBackendCfg>
{
  /**
@@ -87,13 +74,6 @@
       ConfigConstants.NAME_PREFIX_CFG + "je-index";
  /**
   * The name of the object class which configures
   * the database.
   */
  public static final String OBJECT_CLASS_CONFIG_DATABASE =
       ConfigConstants.NAME_PREFIX_CFG + "je-database";
  /**
   * The name of the attribute which configures
   * the attribute type of an attribute index.
   */
@@ -123,14 +103,6 @@
  /**
   * The name of the attribute which configures
   * the backend index entry limit.
   */
  public static final String ATTR_BACKEND_INDEX_ENTRY_LIMIT =
       ConfigConstants.NAME_PREFIX_CFG + "backend-index-entry-limit";
  /**
   * The name of the attribute which configures
   * the subtree delete size limit.
   */
  public static final String ATTR_SUBTREE_DELETE_SIZE_LIMIT =
@@ -196,45 +168,6 @@
  /**
   * A set of units and their multipliers for configuration attributes
   * representing a number of bytes.
   */
  private static HashMap<String, Double> memoryUnits;
  /**
   * A set of units and their multipliers for configuration attributes
   * representing a period of time in milliseconds.
   */
  private static HashMap<String, Double> timeUnits;
  static
  {
    memoryUnits = new HashMap<String, Double>();
    memoryUnits.put(SIZE_UNIT_BYTES_ABBR, 1D);
    memoryUnits.put(SIZE_UNIT_BYTES_FULL, 1D);
    memoryUnits.put(SIZE_UNIT_KILOBYTES_ABBR, 1000D);
    memoryUnits.put(SIZE_UNIT_KILOBYTES_FULL, 1000D);
    memoryUnits.put(SIZE_UNIT_MEGABYTES_ABBR, 1000000D);
    memoryUnits.put(SIZE_UNIT_MEGABYTES_FULL, 1000000D);
    memoryUnits.put(SIZE_UNIT_GIGABYTES_ABBR, 1000000000D);
    memoryUnits.put(SIZE_UNIT_GIGABYTES_FULL, 1000000000D);
    memoryUnits.put(SIZE_UNIT_KIBIBYTES_ABBR, 1024D);
    memoryUnits.put(SIZE_UNIT_KIBIBYTES_FULL, 1024D);
    memoryUnits.put(SIZE_UNIT_MEBIBYTES_ABBR, (double) (1024 * 1024));
    memoryUnits.put(SIZE_UNIT_MEBIBYTES_FULL, (double) (1024 * 1024));
    memoryUnits.put(SIZE_UNIT_GIBIBYTES_ABBR, (double) (1024 * 1024 * 1024));
    memoryUnits.put(SIZE_UNIT_GIBIBYTES_FULL, (double) (1024 * 1024 * 1024));
    timeUnits = new HashMap<String, Double>();
    timeUnits.put(TIME_UNIT_MILLISECONDS_ABBR, 1D);
    timeUnits.put(TIME_UNIT_MILLISECONDS_FULL, 1D);
    timeUnits.put(TIME_UNIT_SECONDS_ABBR, 1000D);
    timeUnits.put(TIME_UNIT_SECONDS_FULL, 1000D);
    timeUnits.put(TIME_UNIT_MINUTES_ABBR, (double) (60 * 1000));
    timeUnits.put(TIME_UNIT_MINUTES_FULL, (double) (60 * 1000));
  }
  /**
   * The set of base DNs.
   */
  private DN[] baseDNs = null;
@@ -248,76 +181,23 @@
   * The backend directory permission mode. By default, owner has read, write
   * and execute permissions on the database directory.
   */
  private FilePermission backendPermission = new FilePermission(0700);
  private FilePermission backendPermission;
  /**
   * Number of times we should retry database transactions that get aborted
   * due to deadlock.
   * The current configuration.
   */
  private int deadlockRetryLimit = 0;
  /**
   * The backend index entry limit, zero means unlimited.
   */
  private int backendIndexEntryLimit = 0;
  /**
   * The maximum number of entries to process in a single pass through the LDIF
   * file.
   */
  private int importPassSize = Integer.MAX_VALUE;
  /**
   * The subtree delete size limit, zero means unlimited.
   */
  private int subtreeDeleteSizeLimit = 0;
  /**
   * The memory available for import buffering.
   */
  private long importBufferSize = 100*1024*1024;
  /**
   * The pathname of the directory for import temporary files.
   */
  private String importTempDirectory = "importTmp";
  private JEBackendCfg currentConfig;
  /**
   * The set of configured attribute indexes.
   */
  private Map<AttributeType, IndexConfig> indexConfigMap = null;
  /**
   * The DN of the JE database environment configuration entry, if any.
   */
  private DN envConfigDN = null;
  /**
   * The JE environment config.
   */
  private EnvironmentConfig envConfig = null;
  /**
   * The import queue size.
   */
  private int importQueueSize = 100;
  /**
   * The number of import threads.
   */
  private int importThreadCount = 8;
  /**
   * The database cache preload time limit in milliseconds.
   */
  private long preloadTimeLimit = 0;
  /**
   * Whether entries should be compressed in the database.
   */
  private boolean entriesCompressed = false;
  /**
@@ -331,28 +211,46 @@
  public void initializeConfig(ConfigEntry configEntry, DN[] baseDNs)
       throws ConfigException
  {
    StringConfigAttribute stub;
    IntegerConfigAttribute intStub;
    IntegerWithUnitConfigAttribute intWithUnitStub;
    initializeConfig(BackendImpl.getJEBackendCfg(configEntry), configEntry,
                     baseDNs);
  }
  /**
   * Initialize this JE backend configuration from a configuration entry.
   *
   * @param  cfg          The backend configuration entry.
   * @param  baseDNs      The set of base DNs that have been configured for this
   *                      backend.
   * @throws ConfigException If there is an error in the configuration entry.
   */
  public void initializeConfig(JEBackendCfg cfg, DN[] baseDNs)
       throws ConfigException
  {
    initializeConfig(cfg, DirectoryServer.getConfigEntry(cfg.dn()), baseDNs);
  }
  /**
   * Initialize this JE backend configuration from a configuration object
   * and its configuration entry.
   *
   * @param  cfg          The backend configuration object.
   * @param  configEntry  The backend configuration entry.
   * @param  baseDNs      The set of base DNs that have been configured for this
   *                      backend.
   * @throws ConfigException If there is an error in the configuration entry.
   */
  private void initializeConfig(JEBackendCfg cfg, ConfigEntry configEntry,
                                DN[] baseDNs)
       throws ConfigException
  {
    // Set the base DNs.
    this.baseDNs = baseDNs;
    // Determine the backend database directory.
    // Required, single-valued config attribute requiring admin action on change
    String msg = getMessage(MSGID_CONFIG_DESCRIPTION_BACKEND_DIRECTORY);
    stub =
         new StringConfigAttribute(ATTR_BACKEND_DIRECTORY,
                                   msg, true, false, true);
    StringConfigAttribute backendDirectoryAttr = (StringConfigAttribute)
         configEntry.getConfigAttribute(stub);
    if (backendDirectoryAttr == null)
    {
      int msgID = MSGID_CONFIG_BACKEND_NO_DIRECTORY;
      String message = getMessage(msgID, configEntry.getDN().toString());
      throw new ConfigException(msgID, message);
    }
    backendDirectory = getFileForPath(backendDirectoryAttr.activeValue());
    backendDirectory = getFileForPath(cfg.getBackendDirectory());
    //Make sure the directory is valid.
    if (!backendDirectory.isDirectory())
    {
@@ -361,177 +259,34 @@
      throw new ConfigException(MSGID_JEB_DIRECTORY_INVALID, message);
    }
    // ds-cfg-backend-mode
    // Optional, single-valued config attribute requiring admin action on change
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_BACKEND_MODE);
    stub =
        new StringConfigAttribute(ATTR_BACKEND_MODE, msg, false,false, true);
    StringConfigAttribute backendModeAttr = (StringConfigAttribute)
         configEntry.getConfigAttribute(stub);
    if (backendModeAttr != null)
    FilePermission newBackendPermission;
    try
    {
      FilePermission newBackendPermission;
      try
      {
        newBackendPermission = FilePermission.decodeUNIXMode(
            backendModeAttr.activeValue());
      }
      catch(Exception e)
      {
        int msgID = MSGID_CONFIG_BACKEND_MODE_INVALID;
        String message = getMessage(msgID, configEntry.getDN().toString());
        throw new ConfigException(msgID, message);
      }
      //Make sure the mode will allow the server itself access to
      //the database
      if(!newBackendPermission.isOwnerWritable() ||
           !newBackendPermission.isOwnerReadable() ||
           !newBackendPermission.isOwnerExecutable())
      {
        int msgID = MSGID_CONFIG_BACKEND_INSANE_MODE;
        String message = getMessage(msgID);
        throw new ConfigException(msgID, message);
      }
      else
      {
        backendPermission = newBackendPermission;
      }
      newBackendPermission =
           FilePermission.decodeUNIXMode(cfg.getBackendMode());
    }
    catch(Exception e)
    {
      int msgID = MSGID_CONFIG_BACKEND_MODE_INVALID;
      String message = getMessage(msgID, cfg.dn().toString());
      throw new ConfigException(msgID, message);
    }
    // ds-cfg-backendIndexEntryLimit
    // Optional, single-valued config attribute requiring admin action on change
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_BACKEND_INDEX_ENTRY_LIMIT);
    intStub =
         new IntegerConfigAttribute(ATTR_BACKEND_INDEX_ENTRY_LIMIT, msg, false,
                                    false, true, true, 0, false, 0);
    IntegerConfigAttribute entryLimitAttr = (IntegerConfigAttribute)
         configEntry.getConfigAttribute(intStub);
    if (entryLimitAttr != null)
    //Make sure the mode will allow the server itself access to
    //the database
    if(!newBackendPermission.isOwnerWritable() ||
         !newBackendPermission.isOwnerReadable() ||
         !newBackendPermission.isOwnerExecutable())
    {
      backendIndexEntryLimit = entryLimitAttr.activeIntValue();
      int msgID = MSGID_CONFIG_BACKEND_INSANE_MODE;
      String message = getMessage(msgID);
      throw new ConfigException(msgID, message);
    }
    // ds-cfg-backendSubtreeDeleteSizeLimit
    // Optional, single-valued config attribute
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_SUBTREE_DELETE_SIZE_LIMIT);
    intStub =
         new IntegerConfigAttribute(ATTR_SUBTREE_DELETE_SIZE_LIMIT, msg, false,
                                    false, false, true, 0, false, 0);
    IntegerConfigAttribute sdslAttr = (IntegerConfigAttribute)
         configEntry.getConfigAttribute(intStub);
    if (sdslAttr != null)
    else
    {
      subtreeDeleteSizeLimit = sdslAttr.activeIntValue();
      backendPermission = newBackendPermission;
    }
    // Determine the directory for import temporary files.
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_IMPORT_TEMP_DIRECTORY);
    stub =
         new StringConfigAttribute(ATTR_IMPORT_TEMP_DIRECTORY,
                                   msg, false, false, false);
    StringConfigAttribute importTempDirAttr = (StringConfigAttribute)
         configEntry.getConfigAttribute(stub);
    if (importTempDirAttr != null)
    {
      this.importTempDirectory = importTempDirAttr.activeValue();
    }
    // Determine the memory available for import buffering.
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_IMPORT_BUFFER_SIZE);
    intWithUnitStub =
         new IntegerWithUnitConfigAttribute(ATTR_IMPORT_BUFFER_SIZE, msg,
                                            false, memoryUnits,
                                            true, 10*1024*1024, false,
                                            0);
    IntegerWithUnitConfigAttribute importBufferSizeAttr =
         (IntegerWithUnitConfigAttribute)
         configEntry.getConfigAttribute(intWithUnitStub);
    if (importBufferSizeAttr != null)
    {
      importBufferSize = importBufferSizeAttr.activeCalculatedValue();
    }
    // Determine the import queue size.
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_IMPORT_QUEUE_SIZE);
    intStub =
         new IntegerConfigAttribute(ATTR_IMPORT_QUEUE_SIZE, msg, false,
                                    false, false, true, 1, false, 0);
    IntegerConfigAttribute a = (IntegerConfigAttribute)
         configEntry.getConfigAttribute(intStub);
    if (a != null)
    {
      importQueueSize = a.activeIntValue();
    }
    // Determine the number of import threads.
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_IMPORT_THREAD_COUNT);
    intStub =
         new IntegerConfigAttribute(ATTR_IMPORT_THREAD_COUNT, msg, false,
                                    false, false, true, 1, false, 0);
    a = (IntegerConfigAttribute)
         configEntry.getConfigAttribute(intStub);
    if (a != null)
    {
      importThreadCount = a.activeIntValue();
    }
    // Determine the import pass size.
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_IMPORT_PASS_SIZE);
    intStub = new IntegerConfigAttribute(ATTR_IMPORT_PASS_SIZE, msg, true,
                                         false, false, true, 0, true,
                                         Integer.MAX_VALUE);
    a = (IntegerConfigAttribute)
         configEntry.getConfigAttribute(intStub);
    if (a != null)
    {
      importPassSize = a.activeIntValue();
    }
    // Determine the database cache preload time limit.
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_PRELOAD_TIME_LIMIT);
    intWithUnitStub =
         new IntegerWithUnitConfigAttribute(ATTR_PRELOAD_TIME_LIMIT, msg,
                                            false, timeUnits,
                                            true, 0, false, 0);
    IntegerWithUnitConfigAttribute preloadTimeLimitAttr =
         (IntegerWithUnitConfigAttribute)
         configEntry.getConfigAttribute(intWithUnitStub);
    if (preloadTimeLimitAttr != null)
    {
      preloadTimeLimit = preloadTimeLimitAttr.activeCalculatedValue();
    }
    // Determine whether entries should be compressed in the database.
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_ENTRIES_COMPRESSED);
    BooleanConfigAttribute booleanStub =
         new BooleanConfigAttribute(ATTR_ENTRIES_COMPRESSED, msg, false);
    BooleanConfigAttribute entriesCompressedAttr =
         (BooleanConfigAttribute)
         configEntry.getConfigAttribute(booleanStub);
    if (entriesCompressedAttr != null)
    {
      entriesCompressed = entriesCompressedAttr.activeValue();
    }
    // Determine the deadlock retry limit.
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_DEADLOCK_RETRY_LIMIT);
    intStub =
         new IntegerConfigAttribute(ATTR_DEADLOCK_RETRY_LIMIT, msg, false,
                                    false, false, true, 0, false, 0);
    a = (IntegerConfigAttribute)
         configEntry.getConfigAttribute(intStub);
    if (a != null)
    {
      deadlockRetryLimit = a.activeIntValue();
    }
    indexConfigMap = new HashMap<AttributeType, IndexConfig>();
    // Create an RDN for cn=Index.
@@ -551,31 +306,12 @@
    ConcurrentHashMap<DN, ConfigEntry> children = configEntry.getChildren();
    for (ConfigEntry childConfigEntry : children.values())
    {
      if (childConfigEntry.hasObjectClass(OBJECT_CLASS_CONFIG_DATABASE))
      {
        // This is a database config entry.
        if (envConfig != null)
        {
          int    msgID   = MSGID_JEB_DUPLICATE_CONFIG_ENTRY;
          String message = getMessage(msgID,
                                      childConfigEntry.getDN().toString(),
                                      OBJECT_CLASS_CONFIG_DATABASE);
          logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.MILD_ERROR,
                   message, msgID);
        }
        else
        {
          envConfig =
               ConfigurableEnvironment.parseConfigEntry(childConfigEntry);
          envConfigDN = childConfigEntry.getDN();
        }
      }
      else if (childConfigEntry.getDN().getRDN().equals(indexRDN))
      if (childConfigEntry.getDN().getRDN().equals(indexRDN))
      {
        // This is the cn=Index branch entry.
        // Determine the index configuration.
        configureIndexEntries(indexConfigMap, backendIndexEntryLimit,
        configureIndexEntries(indexConfigMap, cfg.getBackendIndexEntryLimit(),
                              childConfigEntry.getChildren().values());
      }
      else
@@ -588,11 +324,9 @@
      }
    }
    // Create a database configuration with defaults if we don't have one.
    if (envConfig == null)
    {
      envConfig = ConfigurableEnvironment.defaultConfig();
    }
    envConfig = ConfigurableEnvironment.parseConfigEntry(cfg);
    currentConfig = cfg;
  }
  /**
@@ -828,7 +562,7 @@
   */
  public int getDeadlockRetryLimit()
  {
    return deadlockRetryLimit;
    return currentConfig.getBackendDeadlockRetryLimit();
  }
  /**
@@ -838,7 +572,7 @@
   */
  public int getBackendIndexEntryLimit()
  {
    return backendIndexEntryLimit;
    return currentConfig.getBackendIndexEntryLimit();
  }
  /**
@@ -848,7 +582,7 @@
   */
  public int getSubtreeDeleteSizeLimit()
  {
    return subtreeDeleteSizeLimit;
    return currentConfig.getBackendSubtreeDeleteSizeLimit();
  }
  /**
@@ -868,7 +602,7 @@
   */
  public String getImportTempDirectory()
  {
    return importTempDirectory;
    return currentConfig.getBackendImportTempDirectory();
  }
  /**
@@ -877,7 +611,7 @@
   */
   public long getImportBufferSize()
  {
    return importBufferSize;
    return currentConfig.getBackendImportBufferSize();
  }
  /**
@@ -886,7 +620,7 @@
   */
  public int getImportQueueSize()
  {
    return importQueueSize;
    return currentConfig.getBackendImportQueueSize();
  }
  /**
@@ -895,7 +629,7 @@
   */
  public int getImportThreadCount()
  {
    return importThreadCount;
    return currentConfig.getBackendImportThreadCount();
  }
  /**
@@ -907,7 +641,7 @@
   */
  public int getImportPassSize()
  {
    return importPassSize;
    return currentConfig.getBackendImportPassSize();
  }
@@ -919,7 +653,7 @@
   */
  public boolean isEntriesCompressed()
  {
    return entriesCompressed;
    return currentConfig.isBackendEntriesCompressed();
  }
@@ -930,21 +664,122 @@
   */
  public long getPreloadTimeLimit()
  {
    return preloadTimeLimit;
    return currentConfig.getBackendPreloadTimeLimit();
  }
  /**
   * Get the DN of the configuration entry for the JE environment, if any.
   *
   * @return The configuration entry DN, or null if there is none.
   * {@inheritDoc}
   */
  public DN getEnvConfigDN()
  public boolean isConfigurationChangeAcceptable(
       JEBackendCfg cfg,
       List<String> unacceptableReasons)
  {
    return envConfigDN;
    boolean acceptable = true;
    // This listener does not handle the changes to JE properties.
    //Make sure the directory is valid.
    if (!backendDirectory.isDirectory())
    {
      int msgID = MSGID_JEB_DIRECTORY_INVALID;
      String message = getMessage(msgID, backendDirectory.getPath());
      unacceptableReasons.add(message);
      acceptable = false;
    }
    try
    {
      FilePermission newBackendPermission =
           FilePermission.decodeUNIXMode(cfg.getBackendMode());
      //Make sure the mode will allow the server itself access to
      //the database
      if(!newBackendPermission.isOwnerWritable() ||
           !newBackendPermission.isOwnerReadable() ||
           !newBackendPermission.isOwnerExecutable())
      {
        int msgID = MSGID_CONFIG_BACKEND_INSANE_MODE;
        String message = getMessage(msgID);
        unacceptableReasons.add(message);
        acceptable = false;
      }
    }
    catch(Exception e)
    {
      int msgID = MSGID_CONFIG_BACKEND_MODE_INVALID;
      String message = getMessage(msgID, cfg.dn().toString());
      unacceptableReasons.add(message);
      acceptable = false;
    }
    return acceptable;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(JEBackendCfg cfg)
  {
    ConfigChangeResult ccr;
    ResultCode resultCode = ResultCode.SUCCESS;
    ArrayList<String> messages = new ArrayList<String>();
    try
    {
      // Set the base DNs.
      baseDNs = new DN[cfg.getBackendBaseDN().size()];
      baseDNs = cfg.getBackendBaseDN().toArray(baseDNs);
      // Determine the backend database directory.
      backendDirectory = getFileForPath(cfg.getBackendDirectory());
      FilePermission newPermission =
           FilePermission.decodeUNIXMode(cfg.getBackendMode());
      // Check for changes to the database directory permissions
      FilePermission oldPermission = backendPermission;
      if(FilePermission.canSetPermissions() &&
          !FilePermission.toUNIXMode(oldPermission).equals(
          FilePermission.toUNIXMode(newPermission)))
      {
        try
        {
          if(!FilePermission.setPermissions(backendDirectory,
                                            newPermission))
          {
            throw new Exception();
          }
        }
        catch(Exception e)
        {
          // Log a warning that the permissions were not set.
          int msgID = MSGID_JEB_SET_PERMISSIONS_FAILED;
          String message = getMessage(msgID,
                                      backendDirectory.getPath());
          logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_WARNING,
                   message, msgID);
        }
      }
      backendPermission = newPermission;
      currentConfig = cfg;
    }
    catch (Exception e)
    {
      messages.add(e.getMessage());
      ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
                                   false, messages);
      return ccr;
    }
    ccr = new ConfigChangeResult(resultCode, false, messages);
    return ccr;
  }
}
opends/src/server/org/opends/server/backends/jeb/ConfigurableEnvironment.java
@@ -26,52 +26,30 @@
 */
package org.opends.server.backends.jeb;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.config.ConfigParam;
import com.sleepycat.je.config.EnvironmentParams;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.IntegerConfigAttribute;
import org.opends.server.config.IntegerWithUnitConfigAttribute;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigConstants;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.ResultCode;
import org.opends.server.types.DebugLogLevel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.lang.reflect.Method;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.messages.JebMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.admin.std.server.JEBackendCfg;
import org.opends.server.admin.std.meta.JEBackendCfgDefn;
import org.opends.server.admin.DurationPropertyDefinition;
import org.opends.server.admin.BooleanPropertyDefinition;
import org.opends.server.admin.PropertyDefinition;
/**
 * This class represents a JE environment handle that can be configured by the
 * Directory Server as a configurable component.
 * This class maps JE properties to configuration attributes.
 */
public class ConfigurableEnvironment implements ConfigurableComponent
public class ConfigurableEnvironment
{
  /**
   * The DN of the configuration entry with which the environment is
   * associated.
   */
  private DN configDN;
  /**
   * The JE environment handle.
   */
  private Environment environment;
  /**
   * The name of the attribute which configures the database cache size as a
   * percentage of Java VM heap size.
   */
@@ -183,208 +161,172 @@
  /**
   * A map of JE property names to their associated configuration attribute.
   * A map of JE property names to the corresponding configuration attribute.
   */
  private static HashMap<String, ConfigAttribute> configAttrMap =
       new HashMap<String, ConfigAttribute>();
  private static HashMap<String, String> attrMap =
       new HashMap<String, String>();
  /**
   * A list of registered environment configuration attributes.
   * A map of configuration attribute names to the corresponding configuration
   * object getter method.
   */
  private static ArrayList<ConfigAttribute> configAttrList =
       new ArrayList<ConfigAttribute>();
  private static HashMap<String,Method> methodMap =
       new HashMap<String, Method>();
  private static final ConfigAttribute CONFIG_ATTR_CACHE_PERCENT;
  private static final ConfigAttribute CONFIG_ATTR_CACHE_SIZE;
  private static final ConfigAttribute CONFIG_ATTR_TXN_NO_SYNC;
  private static final ConfigAttribute CONFIG_ATTR_TXN_WRITE_NO_SYNC;
  private static final ConfigAttribute CONFIG_ATTR_RUN_CLEANER;
  private static final ConfigAttribute CONFIG_ATTR_CLEANER_MIN_UTILIZATION;
  private static final ConfigAttribute CONFIG_ATTR_EVICTOR_LRU_ONLY;
  private static final ConfigAttribute CONFIG_ATTR_EVICTOR_NODES_PER_SCAN;
  private static final ConfigAttribute CONFIG_ATTR_LOG_FILE_MAX;
  private static final ConfigAttribute CONFIG_ATTR_LOGGING_FILE_HANDLER_ON;
  private static final ConfigAttribute CONFIG_ATTR_LOGGING_LEVEL;
  private static final ConfigAttribute CONFIG_ATTR_CHECKPOINTER_BYTES_INTERVAL;
  private static final ConfigAttribute CONFIG_ATTR_CHECKPOINTER_WAKEUP_INTERVAL;
  private static final ConfigAttribute CONFIG_ATTR_NUM_LOCK_TABLES;
  private static final ConfigAttribute CONFIG_ATTR_NUM_CLEANER_THREADS;
  /**
   * A map of configuration attribute names to the corresponding configuration
   * PropertyDefinition.
   */
  private static HashMap<String,PropertyDefinition> defnMap =
       new HashMap<String, PropertyDefinition>();
  /**
   * Register an environment property and its associated configuration
   * attribute.
   * Register a JE property and its corresponding configuration attribute.
   *
   * @param propertyName The name of the JE property to be registered.
   * @param configAttr   The configuration attribute associated with the
   *                     property.
   * @param attrName     The name of the configuration attribute associated
   *                     with the property.
   * @throws Exception   If there is an error in the attribute name.
   */
  private static void registerPropertyAttribute(String propertyName,
                                                ConfigAttribute configAttr)
  private static void registerProp(String propertyName, String attrName)
       throws Exception
  {
    configAttrMap.put(propertyName, configAttr);
    configAttrList.add(configAttr);
    // Strip off NAME_PREFIX_CFG.
    String baseName = attrName.substring(7);
    // Convert hyphenated to camel case.
    StringBuilder builder = new StringBuilder();
    boolean capitalize = true;
    for (int i = 0; i < baseName.length(); i++)
    {
      char c = baseName.charAt(i);
      if (c == '-')
      {
        capitalize = true;
      }
      else
      {
        if (capitalize)
        {
          builder.append(Character.toUpperCase(c));
        }
        else
        {
          builder.append(c);
        }
        capitalize = false;
      }
    }
    String methodBaseName = builder.toString();
    Class configClass = JEBackendCfg.class;
    JEBackendCfgDefn defn = JEBackendCfgDefn.getInstance();
    Class defClass = defn.getClass();
    PropertyDefinition propDefn =
         (PropertyDefinition)defClass.getMethod("get" + methodBaseName +
         "PropertyDefinition").invoke(defn);
    String methodName;
    if (propDefn instanceof BooleanPropertyDefinition)
    {
      methodName = "is" + methodBaseName;
    }
    else
    {
      methodName = "get" + methodBaseName;
    }
    defnMap.put(attrName, propDefn);
    methodMap.put(attrName, configClass.getMethod(methodName));
    attrMap.put(propertyName, attrName);
  }
  /**
   * Get the name of the configuration attribute associated with a JE property.
   * @param jeProperty The name of the JE property.
   * @return The name of the associated configuration attribute.
   */
  public static String getAttributeForProperty(String jeProperty)
  {
    return attrMap.get(jeProperty);
  }
  /**
   * Get the value of a JE property that is mapped to a configuration attribute.
   * @param cfg The configuration containing the property values.
   * @param attrName The conriguration attribute type name.
   * @return The string value of the JE property.
   */
  private static String getPropertyValue(JEBackendCfg cfg, String attrName)
  {
    try
    {
      PropertyDefinition propDefn = defnMap.get(attrName);
      Method method = methodMap.get(attrName);
      if (propDefn instanceof DurationPropertyDefinition)
      {
        Long value = (Long)method.invoke(cfg);
        // JE durations are in microseconds so we must convert.
        DurationPropertyDefinition durationPropDefn =
             (DurationPropertyDefinition)propDefn;
        value = 1000*durationPropDefn.getBaseUnit().getDuration(value);
        return String.valueOf(value);
      }
      else
      {
        Object value = method.invoke(cfg);
        return String.valueOf(value);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      return "";
    }
  }
  static
  {
    HashMap<String, Double> memoryUnits = new HashMap<String, Double>();
    memoryUnits.put(SIZE_UNIT_BYTES_ABBR, 1D);
    memoryUnits.put(SIZE_UNIT_BYTES_FULL, 1D);
    memoryUnits.put(SIZE_UNIT_KILOBYTES_ABBR, 1000D);
    memoryUnits.put(SIZE_UNIT_KILOBYTES_FULL, 1000D);
    memoryUnits.put(SIZE_UNIT_MEGABYTES_ABBR, 1000000D);
    memoryUnits.put(SIZE_UNIT_MEGABYTES_FULL, 1000000D);
    memoryUnits.put(SIZE_UNIT_GIGABYTES_ABBR, 1000000000D);
    memoryUnits.put(SIZE_UNIT_GIGABYTES_FULL, 1000000000D);
    memoryUnits.put(SIZE_UNIT_KIBIBYTES_ABBR, 1024D);
    memoryUnits.put(SIZE_UNIT_KIBIBYTES_FULL, 1024D);
    memoryUnits.put(SIZE_UNIT_MEBIBYTES_ABBR, (double) (1024 * 1024));
    memoryUnits.put(SIZE_UNIT_MEBIBYTES_FULL, (double) (1024 * 1024));
    memoryUnits.put(SIZE_UNIT_GIBIBYTES_ABBR, (double) (1024 * 1024 * 1024));
    memoryUnits.put(SIZE_UNIT_GIBIBYTES_FULL, (double) (1024 * 1024 * 1024));
    // JE time intervals are expressed in microseconds.
    HashMap<String, Double> timeUnits = new HashMap<String, Double>();
    timeUnits.put(TIME_UNIT_MICROSECONDS_ABBR, 1D);
    timeUnits.put(TIME_UNIT_MICROSECONDS_FULL, 1D);
    timeUnits.put(TIME_UNIT_MILLISECONDS_ABBR, 1000D);
    timeUnits.put(TIME_UNIT_MILLISECONDS_FULL, 1000D);
    timeUnits.put(TIME_UNIT_SECONDS_ABBR, 1000000D);
    timeUnits.put(TIME_UNIT_SECONDS_FULL, 1000000D);
    timeUnits.put(TIME_UNIT_MINUTES_ABBR, (double) (60 * 1000000));
    timeUnits.put(TIME_UNIT_MINUTES_FULL, (double) (60 * 1000000));
    String msg;
    // Create configuration attributes for the JE properties that
    // can be configured through the Directory Server interfaces.
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_TXN_NO_SYNC);
    CONFIG_ATTR_TXN_NO_SYNC =
         new BooleanConfigAttribute(ATTR_DATABASE_TXN_NO_SYNC,
                                    msg, false);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_TXN_WRITE_NO_SYNC);
    CONFIG_ATTR_TXN_WRITE_NO_SYNC =
         new BooleanConfigAttribute(ATTR_DATABASE_TXN_WRITE_NO_SYNC,
                                    msg, false);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_CACHE_PERCENT);
    CONFIG_ATTR_CACHE_PERCENT =
         new IntegerConfigAttribute(ATTR_DATABASE_CACHE_PERCENT, msg, true,
                                    false, false, true, 1, true, 90);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_CACHE_SIZE);
    CONFIG_ATTR_CACHE_SIZE =
         new IntegerWithUnitConfigAttribute(ATTR_DATABASE_CACHE_SIZE, msg,
                                            false, memoryUnits,
                                            true, 0, false, 0);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_RUN_CLEANER);
    CONFIG_ATTR_RUN_CLEANER =
         new BooleanConfigAttribute(ATTR_DATABASE_RUN_CLEANER,
                                    msg, false);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_CLEANER_MIN_UTILIZATION);
    CONFIG_ATTR_CLEANER_MIN_UTILIZATION =
         new IntegerConfigAttribute(ATTR_CLEANER_MIN_UTILIZATION, msg, true,
                                    false, true, true, 0, true, 100);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_EVICTOR_LRU_ONLY);
    CONFIG_ATTR_EVICTOR_LRU_ONLY =
         new BooleanConfigAttribute(ATTR_EVICTOR_LRU_ONLY, msg, true);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_EVICTOR_NODES_PER_SCAN);
    CONFIG_ATTR_EVICTOR_NODES_PER_SCAN =
         new IntegerConfigAttribute(ATTR_EVICTOR_NODES_PER_SCAN, msg, false,
                                    false, true, true, 1, true, 1000);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_LOG_FILE_MAX);
    CONFIG_ATTR_LOG_FILE_MAX =
         new IntegerWithUnitConfigAttribute(ATTR_DATABASE_LOG_FILE_MAX, msg,
                                            false, memoryUnits,
                                            true, 1000000, true, 4294967296L);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_LOGGING_FILE_HANDLER_ON);
    CONFIG_ATTR_LOGGING_FILE_HANDLER_ON =
         new BooleanConfigAttribute(ATTR_LOGGING_FILE_HANDLER_ON, msg, true);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_LOGGING_LEVEL);
    CONFIG_ATTR_LOGGING_LEVEL =
         new StringConfigAttribute(ATTR_LOGGING_LEVEL, msg, false, false, true);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_CHECKPOINT_BYTES_INTERVAL);
    CONFIG_ATTR_CHECKPOINTER_BYTES_INTERVAL =
         new IntegerWithUnitConfigAttribute(ATTR_CHECKPOINTER_BYTES_INTERVAL,
                                            msg, true, memoryUnits,
                                            true, 0, false, 0);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_CHECKPOINT_WAKEUP_INTERVAL);
    CONFIG_ATTR_CHECKPOINTER_WAKEUP_INTERVAL =
         new IntegerWithUnitConfigAttribute(ATTR_CHECKPOINTER_WAKEUP_INTERVAL,
                                            msg, true, timeUnits,
                                            true, 1000000, false, 0);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_NUM_LOCK_TABLES);
    CONFIG_ATTR_NUM_LOCK_TABLES =
         new IntegerConfigAttribute(ATTR_NUM_LOCK_TABLES, msg, false,
                                    false, true, true, 1, true, 32767);
    msg = getMessage(MSGID_CONFIG_DESCRIPTION_NUM_CLEANER_THREADS);
    CONFIG_ATTR_NUM_CLEANER_THREADS =
         new IntegerConfigAttribute(ATTR_NUM_CLEANER_THREADS, msg, false,
                                    false, false, true, 1, false, 0);
    // Register the parameters that have JE property names.
    registerPropertyAttribute("je.maxMemoryPercent",
                              CONFIG_ATTR_CACHE_PERCENT);
    registerPropertyAttribute("je.maxMemory",
                              CONFIG_ATTR_CACHE_SIZE);
    registerPropertyAttribute("je.cleaner.minUtilization",
                              CONFIG_ATTR_CLEANER_MIN_UTILIZATION);
    registerPropertyAttribute("je.env.runCleaner",
                              CONFIG_ATTR_RUN_CLEANER);
    registerPropertyAttribute("je.evictor.lruOnly",
                              CONFIG_ATTR_EVICTOR_LRU_ONLY);
    registerPropertyAttribute("je.evictor.nodesPerScan",
                              CONFIG_ATTR_EVICTOR_NODES_PER_SCAN);
    registerPropertyAttribute("je.log.fileMax",
                              CONFIG_ATTR_LOG_FILE_MAX);
    registerPropertyAttribute("java.util.logging.FileHandler.on",
                              CONFIG_ATTR_LOGGING_FILE_HANDLER_ON);
    registerPropertyAttribute("java.util.logging.level",
                              CONFIG_ATTR_LOGGING_LEVEL);
    registerPropertyAttribute("je.checkpointer.bytesInterval",
                              CONFIG_ATTR_CHECKPOINTER_BYTES_INTERVAL);
    registerPropertyAttribute("je.checkpointer.wakeupInterval",
                              CONFIG_ATTR_CHECKPOINTER_WAKEUP_INTERVAL);
    registerPropertyAttribute("je.lock.nLockTables",
                              CONFIG_ATTR_NUM_LOCK_TABLES);
    registerPropertyAttribute("je.cleaner.threads",
                              CONFIG_ATTR_NUM_CLEANER_THREADS);
    // These parameters do not have JE property names.
    configAttrList.add(CONFIG_ATTR_TXN_NO_SYNC);
    configAttrList.add(CONFIG_ATTR_TXN_WRITE_NO_SYNC);
  }
  /**
   * Constructs a configurable environment.
   *
   * @param configDN    The DN of the configuration entry with which the
   *                    environment is associated.
   * @param environment The JE environment handle.
   */
  public ConfigurableEnvironment(DN configDN, Environment environment)
  {
    this.configDN = configDN;
    this.environment = environment;
    try
    {
      registerProp("je.maxMemoryPercent", ATTR_DATABASE_CACHE_PERCENT);
      registerProp("je.maxMemory", ATTR_DATABASE_CACHE_SIZE);
      registerProp("je.cleaner.minUtilization", ATTR_CLEANER_MIN_UTILIZATION);
      registerProp("je.env.runCleaner", ATTR_DATABASE_RUN_CLEANER);
      registerProp("je.evictor.lruOnly", ATTR_EVICTOR_LRU_ONLY);
      registerProp("je.evictor.nodesPerScan", ATTR_EVICTOR_NODES_PER_SCAN);
      registerProp("je.log.fileMax", ATTR_DATABASE_LOG_FILE_MAX);
      registerProp("java.util.logging.FileHandler.on",
                   ATTR_LOGGING_FILE_HANDLER_ON);
      registerProp("java.util.logging.level", ATTR_LOGGING_LEVEL);
      registerProp("je.checkpointer.bytesInterval",
                   ATTR_CHECKPOINTER_BYTES_INTERVAL);
      registerProp("je.checkpointer.wakeupInterval",
                   ATTR_CHECKPOINTER_WAKEUP_INTERVAL);
      registerProp("je.lock.nLockTables", ATTR_NUM_LOCK_TABLES);
      registerProp("je.cleaner.threads", ATTR_NUM_CLEANER_THREADS);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
    }
  }
@@ -417,72 +359,31 @@
  /**
   * Parse a configuration entry associated with a JE environment and create an
   * Parse a configuration associated with a JE environment and create an
   * environment config from it.
   *
   * @param configEntry The configuration entry to be parsed.
   * @param cfg The configuration to be parsed.
   * @return An environment config instance corresponding to the config entry.
   * @throws ConfigException If there is an error in the provided configuration
   * entry.
   */
  public static EnvironmentConfig parseConfigEntry(ConfigEntry configEntry)
  public static EnvironmentConfig parseConfigEntry(JEBackendCfg cfg)
       throws ConfigException
  {
    EnvironmentConfig envConfig = defaultConfig();
    // Handle the attributes that do not have a JE property.
    BooleanConfigAttribute booleanAttr;
    booleanAttr = (BooleanConfigAttribute)
         configEntry.getConfigAttribute(CONFIG_ATTR_TXN_NO_SYNC);
    if (booleanAttr != null)
    {
      envConfig.setTxnNoSync(booleanAttr.activeValue());
    }
    booleanAttr = (BooleanConfigAttribute)
         configEntry.getConfigAttribute(CONFIG_ATTR_TXN_WRITE_NO_SYNC);
    if (booleanAttr != null)
    {
      envConfig.setTxnWriteNoSync(booleanAttr.activeValue());
    }
    envConfig.setTxnNoSync(cfg.isDatabaseTxnNoSync());
    envConfig.setTxnWriteNoSync(cfg.isDatabaseTxnWriteNoSync());
    // Iterate through the config attributes associated with a JE property.
    for (Map.Entry<String, ConfigAttribute> mapEntry : configAttrMap.entrySet())
    for (Map.Entry<String, String> mapEntry : attrMap.entrySet())
    {
      String property = mapEntry.getKey();
      ConfigAttribute stub = mapEntry.getValue();
      String jeProperty = mapEntry.getKey();
      String attrName = mapEntry.getValue();
      // Check if the config entry contains this attribute.
      ConfigAttribute configAttr = configEntry.getConfigAttribute(stub);
      if (stub != null)
      {
        // Set the property.
        if (configAttr instanceof BooleanConfigAttribute)
        {
          BooleanConfigAttribute attr = (BooleanConfigAttribute) configAttr;
          boolean value = attr.activeValue();
          envConfig.setConfigParam(property, String.valueOf(value));
        }
        else if (configAttr instanceof IntegerConfigAttribute)
        {
          IntegerConfigAttribute attr = (IntegerConfigAttribute) configAttr;
          long value = attr.activeValue();
          envConfig.setConfigParam(property, String.valueOf(value));
        }
        else if (configAttr instanceof IntegerWithUnitConfigAttribute)
        {
          IntegerWithUnitConfigAttribute attr =
               (IntegerWithUnitConfigAttribute) configAttr;
          long value = attr.activeCalculatedValue();
          envConfig.setConfigParam(property, String.valueOf(value));
        }
        else if (configAttr instanceof StringConfigAttribute)
        {
          StringConfigAttribute attr = (StringConfigAttribute) configAttr;
          String value = attr.activeValue();
          envConfig.setConfigParam(property, value);
        }
      }
      String value = getPropertyValue(cfg, attrName);
      envConfig.setConfigParam(jeProperty, value);
    }
    return envConfig;
@@ -490,122 +391,4 @@
  /**
   * Retrieves the DN of the configuration entry with which this component is
   * associated.
   *
   * @return The DN of the configuration entry with which this component is
   *         associated.
   */
  public DN getConfigurableComponentEntryDN()
  {
    return configDN;
  }
  /**
   * Retrieves the set of configuration attributes that are associated with this
   * configurable component.
   *
   * @return The set of configuration attributes that are associated with this
   *         configurable component.
   */
  public List<ConfigAttribute> getConfigurationAttributes()
  {
    return configAttrList;
  }
  /**
   * Indicates whether the provided configuration entry has an acceptable
   * configuration for this component.  If it does not, then detailed
   * information about the problem(s) should be added to the provided list.
   *
   * @param configEntry         The configuration entry for which to make the
   *                            determination.
   * @param unacceptableReasons A list that can be used to hold messages about
   *                            why the provided entry does not have an
   *                            acceptable configuration.
   * @return <CODE>true</CODE> if the provided entry has an acceptable
   *         configuration for this component, or <CODE>false</CODE> if not.
   */
  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
                                            List<String> unacceptableReasons)
  {
    return true;
  }
  /**
   * Makes a best-effort attempt to apply the configuration contained in the
   * provided entry.  Information about the result of this processing should be
   * added to the provided message list.  Information should always be added to
   * this list if a configuration change could not be applied.  If detailed
   * results are requested, then information about the changes applied
   * successfully (and optionally about parameters that were not changed) should
   * also be included.
   *
   * @param configEntry     The entry containing the new configuration to apply
   *                        for this component.
   * @param detailedResults Indicates whether detailed information about the
   *                        processing should be added to the list.
   *
   * @return  Information about the result of the configuration update.
   */
  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
                                                  boolean detailedResults)
  {
    ConfigChangeResult ccr;
    boolean adminActionRequired = false;
    ArrayList<String> messages = new ArrayList<String>();
    try
    {
      // Check if any JE non-mutable properties were changed.
      EnvironmentConfig oldEnvConfig = environment.getConfig();
      EnvironmentConfig newEnvConfig = parseConfigEntry(configEntry);
      Map paramsMap = EnvironmentParams.SUPPORTED_PARAMS;
      for (Object o : paramsMap.values())
      {
        ConfigParam param = (ConfigParam) o;
        if (!param.isMutable())
        {
          String oldValue = oldEnvConfig.getConfigParam(param.getName());
          String newValue = newEnvConfig.getConfigParam(param.getName());
          if (!oldValue.equalsIgnoreCase(newValue))
          {
            adminActionRequired = true;
            if (detailedResults)
            {
              ConfigAttribute configAttr = configAttrMap.get(param.getName());
              if (configAttr != null)
              {
                int msgID = MSGID_JEB_CONFIG_ATTR_REQUIRES_RESTART;
                messages.add(getMessage(msgID, configAttr.getName()));
              }
            }
          }
        }
      }
      // This takes care of changes to the JE environment for those
      // properties that are mutable at runtime.
      environment.setMutableConfig(newEnvConfig);
    }
    catch (Exception e)
    {
      messages.add(e.getMessage());
      ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
                                   adminActionRequired,
                                   messages);
      return ccr;
    }
    ccr = new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
                                 messages);
    return ccr;
  }
}
opends/src/server/org/opends/server/backends/jeb/RootContainer.java
@@ -42,6 +42,8 @@
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.FilePermission;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.ResultCode;
import static org.opends.server.loggers.Error.logError;
import static org.opends.server.loggers.debug.DebugLogger.debugInfo;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
@@ -57,7 +59,12 @@
    MSGID_JEB_CLEAN_DATABASE_FINISH;
import static org.opends.server.messages.JebMessages.
    MSGID_JEB_SET_PERMISSIONS_FAILED;
import static org.opends.server.messages.JebMessages.
     MSGID_JEB_CONFIG_ATTR_REQUIRES_RESTART;
import org.opends.server.api.Backend;
import org.opends.server.admin.std.server.JEBackendCfg;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.core.DirectoryServer;
/**
 * Wrapper class for the JE environment. Root container holds all the entry
@@ -65,6 +72,7 @@
 * of the entry containers.
 */
public class RootContainer
     implements ConfigurationChangeListener<JEBackendCfg>
{
  /**
@@ -88,12 +96,6 @@
  private DatabaseEnvironmentMonitor monitor;
  /**
   * A configurable component to handle changes to the configuration of
   * the database environment.
   */
  private ConfigurableEnvironment configurableEnv;
  /**
   * The base DNs contained in this entryContainer.
   */
  private ConcurrentHashMap<DN, EntryContainer> entryContainers;
@@ -114,7 +116,6 @@
  public RootContainer(Config config, Backend backend)
  {
    this.env = null;
    this.configurableEnv = null;
    this.monitor = null;
    this.entryContainers = new ConcurrentHashMap<DN, EntryContainer>();
    this.backend = backend;
@@ -288,7 +289,7 @@
   */
  public void openEntryContainers(DN[] baseDNs) throws DatabaseException
  {
    EntryID id = null;
    EntryID id;
    EntryID highestID = null;
    for(DN baseDN : baseDNs)
    {
@@ -331,26 +332,6 @@
  }
  /**
   * Get the ConfigurableEnvironment object for JE environment used by this
   * root container.
   *
   * @return The ConfigurableEnvironment object.
   */
  public ConfigurableEnvironment getConfigurableEnvironment()
  {
    if(configurableEnv == null)
    {
      DN envConfigDN = config.getEnvConfigDN();
      if (envConfigDN != null)
      {
        configurableEnv = new ConfigurableEnvironment(envConfigDN, env);
      }
    }
    return configurableEnv;
  }
  /**
   * Get the DatabaseEnvironmentMonitor object for JE environment used by this
   * root container.
   *
@@ -559,77 +540,6 @@
  }
  /**
   * Apply new configuration to the JE environment.
   *
   * @param newConfig The new configuration to apply.
   * @throws DatabaseException If an error occurs while applying the new
   *                           configuration.
   */
  public void applyNewConfig(Config newConfig) throws DatabaseException
  {
    // Check for changes to the database directory permissions
    FilePermission oldPermission = config.getBackendPermission();
    FilePermission newPermission = newConfig.getBackendPermission();
    if(FilePermission.canSetPermissions() &&
        !FilePermission.toUNIXMode(oldPermission).equals(
        FilePermission.toUNIXMode(newPermission)))
    {
      try
      {
        if(!FilePermission.setPermissions(newConfig.getBackendDirectory(),
                                          newPermission))
        {
          throw new Exception();
        }
      }
      catch(Exception e)
      {
        // Log an warning that the permissions were not set.
        int msgID = MSGID_JEB_SET_PERMISSIONS_FAILED;
        String message = getMessage(msgID,
                                    config.getBackendDirectory().getPath());
        logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_WARNING,
                 message, msgID);
      }
    }
    // Check if any JE non-mutable properties were changed.
    EnvironmentConfig oldEnvConfig = this.config.getEnvironmentConfig();
    EnvironmentConfig newEnvConfig = newConfig.getEnvironmentConfig();
    Map paramsMap = EnvironmentParams.SUPPORTED_PARAMS;
    for (Object o : paramsMap.values())
    {
      ConfigParam param = (ConfigParam)o;
      if (!param.isMutable())
      {
        String oldValue = oldEnvConfig.getConfigParam(param.getName());
        String newValue = newEnvConfig.getConfigParam(param.getName());
        if (!oldValue.equalsIgnoreCase(newValue))
        {
          if(debugEnabled())
          {
            debugInfo("The change to the following property will " +
                      "take effect when the backend is restarted: " +
                      param.getName());
          }
        }
      }
    }
    // This takes care of changes to the JE environment for those
    // properties that are mutable at runtime.
    env.setMutableConfig(newConfig.getEnvironmentConfig());
    config = newConfig;
    if (debugEnabled())
    {
      debugInfo(env.getConfig().toString());
    }
  }
  /**
   * Get the environment stats of the JE environment used in this root
   * container.
   *
@@ -705,4 +615,99 @@
  {
    return (nextid.get() - 1);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
       JEBackendCfg cfg,
       List<String> unacceptableReasons)
  {
    boolean acceptable = true;
    // This listener handles only the changes to JE properties.
    try
    {
      ConfigurableEnvironment.parseConfigEntry(cfg);
    }
    catch (Exception e)
    {
      unacceptableReasons.add(e.getMessage());
      acceptable = false;
    }
    return acceptable;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(JEBackendCfg cfg)
  {
    ConfigChangeResult ccr;
    boolean adminActionRequired = false;
    ArrayList<String> messages = new ArrayList<String>();
    try
    {
      // Check if any JE non-mutable properties were changed.
      EnvironmentConfig oldEnvConfig = env.getConfig();
      EnvironmentConfig newEnvConfig =
           ConfigurableEnvironment.parseConfigEntry(cfg);
      Map paramsMap = EnvironmentParams.SUPPORTED_PARAMS;
      for (Object o : paramsMap.values())
      {
        ConfigParam param = (ConfigParam) o;
        if (!param.isMutable())
        {
          String oldValue = oldEnvConfig.getConfigParam(param.getName());
          String newValue = newEnvConfig.getConfigParam(param.getName());
          if (!oldValue.equalsIgnoreCase(newValue))
          {
            adminActionRequired = true;
            String configAttr = ConfigurableEnvironment.
                 getAttributeForProperty(param.getName());
            if (configAttr != null)
            {
              int msgID = MSGID_JEB_CONFIG_ATTR_REQUIRES_RESTART;
              messages.add(getMessage(msgID, configAttr));
            }
            if(debugEnabled())
            {
              debugInfo("The change to the following property will " +
                        "take effect when the backend is restarted: " +
                        param.getName());
            }
          }
        }
      }
      // This takes care of changes to the JE environment for those
      // properties that are mutable at runtime.
      env.setMutableConfig(newEnvConfig);
      if (debugEnabled())
      {
        debugInfo(env.getConfig().toString());
      }
    }
    catch (Exception e)
    {
      messages.add(e.getMessage());
      ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
                                   adminActionRequired,
                                   messages);
      return ccr;
    }
    ccr = new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
                                 messages);
    return ccr;
  }
}
opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -31,24 +31,19 @@
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import org.opends.server.api.Backend;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.IntegerWithUnitConfigAttribute;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.BackendConfigManager;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.BackupConfig;
@@ -70,9 +65,11 @@
import org.opends.server.types.DebugLogLevel;
import static org.opends.server.messages.BackendMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.util.Validator;
import org.opends.server.admin.std.server.TaskBackendCfg;
import org.opends.server.admin.std.meta.TaskBackendCfgDefn;
import org.opends.server.admin.server.ConfigurationChangeListener;
/**
@@ -82,16 +79,10 @@
 */
public class TaskBackend
       extends Backend
       implements ConfigurableComponent
       implements ConfigurationChangeListener<TaskBackendCfg>
{
  /**
   * The set of time units that will be used for expressing the task retention
   * time.
   */
  private static final LinkedHashMap<String,Double> timeUnits =
       new LinkedHashMap<String,Double>();
  // The current configuration state.
  private TaskBackendCfg currentConfig;
  // The DN of the configuration entry for this backend.
  private DN configEntryDN;
@@ -129,22 +120,6 @@
  static
  {
    timeUnits.put(TIME_UNIT_SECONDS_ABBR, 1D);
    timeUnits.put(TIME_UNIT_SECONDS_FULL, 1D);
    timeUnits.put(TIME_UNIT_MINUTES_ABBR, 60D);
    timeUnits.put(TIME_UNIT_MINUTES_FULL, 60D);
    timeUnits.put(TIME_UNIT_HOURS_ABBR, (double) (60 * 60));
    timeUnits.put(TIME_UNIT_HOURS_FULL, (double) (60 * 60));
    timeUnits.put(TIME_UNIT_DAYS_ABBR, (double) (60 * 60 * 24));
    timeUnits.put(TIME_UNIT_DAYS_FULL, (double) (60 * 60 * 24));
    timeUnits.put(TIME_UNIT_WEEKS_ABBR, (double) (60 * 60 * 24 * 7));
    timeUnits.put(TIME_UNIT_WEEKS_FULL, (double) (60 * 60 * 24 * 7));
  }
  /**
   * Creates a new backend with the provided information.  All backend
   * implementations must implement a default constructor that use
@@ -158,36 +133,16 @@
  }
  /**
   * Initializes this backend based on the information in the provided
   * configuration entry.
   *
   * @param  configEntry  The configuration entry that contains the information
   *                      to use to initialize this backend.
   * @param  baseDNs      The set of base DNs that have been configured for this
   *                      backend.
   *
   * @throws  ConfigException  If an unrecoverable problem arises in the
   *                           process of performing the initialization.
   *
   * @throws  InitializationException  If a problem occurs during initialization
   *                                   that is not related to the server
   *                                   configuration.
   * {@inheritDoc}
   */
  public void initializeBackend(ConfigEntry configEntry, DN[] baseDNs)
         throws ConfigException, InitializationException
  {
    // Make sure that a configuration entry was provided.  If not, then we will
    // not be able to complete the initialization.
    if (configEntry == null)
    {
      int    msgID   = MSGID_TASKBE_CONFIG_ENTRY_NULL;
      String message = getMessage(msgID);
      throw new ConfigException(msgID, message);
    }
    Validator.ensureNotNull(configEntry);
    TaskBackendCfg cfg = getTaskBackendCfg(configEntry);
    configEntryDN = configEntry.getDN();
    configEntryDN = cfg.dn();
    // Make sure that the provided set of base DNs contains exactly one value.
@@ -256,68 +211,11 @@
    // Get the retention time that will be used to determine how long task
    // information stays around once the associated task is completed.
    int msgID = MSGID_TASKBE_DESCRIPTION_RETENTION_TIME;
    IntegerWithUnitConfigAttribute retentionStub =
         new IntegerWithUnitConfigAttribute(ATTR_TASK_RETENTION_TIME,
                                            getMessage(msgID), false, timeUnits,
                                            true, 0, false, 0);
    try
    {
      IntegerWithUnitConfigAttribute retentionAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(retentionStub);
      if (retentionAttr == null)
      {
        retentionTime = DEFAULT_TASK_RETENTION_TIME;
      }
      else
      {
        retentionTime = retentionAttr.activeCalculatedValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_TASKBE_CANNOT_INITIALIZE_RETENTION_TIME;
      String message = getMessage(msgID, stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    retentionTime = cfg.getTaskRetentionTime();
    // Get the path to the task data backing file.
    msgID = MSGID_TASKBE_DESCRIPTION_BACKING_FILE;
    StringConfigAttribute taskFileStub =
         new StringConfigAttribute(ATTR_TASK_BACKING_FILE, getMessage(msgID),
                                   true, false, false);
    try
    {
      StringConfigAttribute taskFileAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(taskFileStub);
      if (taskFileAttr == null)
      {
        taskBackingFile = DirectoryServer.getServerRoot() + File.separator +
                          CONFIG_DIR_NAME + File.separator + TASK_FILE_NAME;
      }
      else
      {
        taskBackingFile = taskFileAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_TASKBE_CANNOT_INITIALIZE_BACKING_FILE;
      String message = getMessage(msgID, stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    taskBackingFile = cfg.getTaskBackingFile();
    // Create the scheduler and initialize it from the backing file.
@@ -331,7 +229,8 @@
    // Register with the Directory Server as a configurable component.
    DirectoryServer.registerConfigurableComponent(this);
    currentConfig = cfg;
    cfg.addTaskChangeListener(this);
    // Register the task base as a private suffix.
@@ -346,7 +245,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
      int msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
      String message = getMessage(msgID, taskRootDN.toString(),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
@@ -368,7 +267,7 @@
   */
  public void finalizeBackend()
  {
    DirectoryServer.deregisterConfigurableComponent(this);
    currentConfig.removeTaskChangeListener(this);
    try
@@ -1029,17 +928,7 @@
  /**
   * Exports the contents of this backend to LDIF.  This method should only be
   * called if <CODE>supportsLDIFExport</CODE> returns <CODE>true</CODE>.  Note
   * that the server will not explicitly initialize this backend before calling
   * this method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  baseDNs       The set of base DNs configured for this backend.
   * @param  exportConfig  The configuration to use when performing the export.
   *
   * @throws  DirectoryException  If a problem occurs while performing the LDIF
   *                              export.
   * {@inheritDoc}
   */
  public void exportLDIF(ConfigEntry configEntry, DN[] baseDNs,
                         LDIFExportConfig exportConfig)
@@ -1066,17 +955,7 @@
  /**
   * Imports information from an LDIF file into this backend.  This method
   * should only be called if <CODE>supportsLDIFImport</CODE> returns
   * <CODE>true</CODE>.  Note that the server will not explicitly initialize
   * this backend before calling this method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  baseDNs       The set of base DNs configured for this backend.
   * @param  importConfig  The configuration to use when performing the import.
   *
   * @throws  DirectoryException  If a problem occurs while performing the LDIF
   *                              import.
   * {@inheritDoc}
   */
  public void importLDIF(ConfigEntry configEntry, DN[] baseDNs,
                         LDIFImportConfig importConfig)
@@ -1135,17 +1014,7 @@
  /**
   * Creates a backup of the contents of this backend in a form that may be
   * restored at a later date if necessary.  This method should only be called
   * if <CODE>supportsBackup</CODE> returns <CODE>true</CODE>.  Note that the
   * server will not explicitly initialize this backend before calling this
   * method.
   *
   * @param  configEntry   The configuration entry for this backend.
   * @param  backupConfig  The configuration to use when performing the backup.
   *
   * @throws  DirectoryException  If a problem occurs while performing the
   *                              backup.
   * {@inheritDoc}
   */
  public void createBackup(ConfigEntry configEntry, BackupConfig backupConfig)
         throws DirectoryException
@@ -1191,17 +1060,7 @@
  /**
   * Restores a backup of the contents of this backend.  This method should only
   * be called if <CODE>supportsRestore</CODE> returns <CODE>true</CODE>.  Note
   * that the server will not explicitly initialize this backend before calling
   * this method.
   *
   * @param  configEntry    The configuration entry for this backend.
   * @param  restoreConfig  The configuration to use when performing the
   *                        restore.
   *
   * @throws  DirectoryException  If a problem occurs while performing the
   *                              restore.
   * {@inheritDoc}
   */
  public void restoreBackup(ConfigEntry configEntry,
                            RestoreConfig restoreConfig)
@@ -1213,116 +1072,48 @@
  /**
   * Retrieves the DN of the configuration entry with which this component is
   * associated.
   *
   * @return  The DN of the configuration entry with which this component is
   *          associated.
   * {@inheritDoc}
   */
  public DN getConfigurableComponentEntryDN()
  {
    return configEntryDN;
  }
  /**
   * Retrieves the set of configuration attributes that are associated with this
   * configurable component.
   *
   * @return  The set of configuration attributes that are associated with this
   *          configurable component.
   */
  public List<ConfigAttribute> getConfigurationAttributes()
  {
    LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
    String description = getMessage(MSGID_TASKBE_DESCRIPTION_BACKING_FILE);
    attrList.add(new StringConfigAttribute(ATTR_TASK_BACKING_FILE, description,
                                           true, false, false,
                                           taskBackingFile));
    description = getMessage(MSGID_TASKBE_DESCRIPTION_RETENTION_TIME);
    attrList.add(new IntegerWithUnitConfigAttribute(ATTR_TASK_RETENTION_TIME,
                                                    description, false,
                                                    timeUnits, true, 0, false,
                                                    0, retentionTime,
                                                    TIME_UNIT_SECONDS_FULL));
    return attrList;
  }
  /**
   * Indicates whether the provided configuration entry has an acceptable
   * configuration for this component.  If it does not, then detailed
   * information about the problem(s) should be added to the provided list.
   *
   * @param  configEntry          The configuration entry for which to make the
   *                              determination.
   * @param  unacceptableReasons  A list that can be used to hold messages about
   *                              why the provided entry does not have an
   *                              acceptable configuration.
   *
   * @return  <CODE>true</CODE> if the provided entry has an acceptable
   *          configuration for this component, or <CODE>false</CODE> if not.
   */
  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
  public boolean isConfigurationChangeAcceptable(TaskBackendCfg configEntry,
                                            List<String> unacceptableReasons)
  {
    boolean configIsAcceptable = true;
    String description = getMessage(MSGID_TASKBE_DESCRIPTION_BACKING_FILE);
    StringConfigAttribute backingStub =
         new StringConfigAttribute(ATTR_TASK_BACKING_FILE, description, true,
                                   false, false);
    try
    {
      StringConfigAttribute backingAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(backingStub);
      if (backingAttr == null)
      String tmpBackingFile = configEntry.getTaskBackingFile();
      if (! taskBackingFile.equals(tmpBackingFile))
      {
        int msgID = MSGID_TASKBE_NO_BACKING_FILE;
        unacceptableReasons.add(getMessage(msgID, ATTR_TASK_BACKING_FILE));
        configIsAcceptable = false;
      }
      else
      {
        String tmpBackingFile = backingAttr.pendingValue();
        if (! taskBackingFile.equals(tmpBackingFile))
        File f = getFileForPath(tmpBackingFile);
        if (f.exists())
        {
          File f = getFileForPath(tmpBackingFile);
          if (f.exists())
          int msgID = MSGID_TASKBE_BACKING_FILE_EXISTS;
          unacceptableReasons.add(getMessage(msgID, tmpBackingFile));
          configIsAcceptable = false;
        }
        else
        {
          File p = f.getParentFile();
          if (p == null)
          {
            int msgID = MSGID_TASKBE_BACKING_FILE_EXISTS;
            int msgID = MSGID_TASKBE_INVALID_BACKING_FILE_PATH;
            unacceptableReasons.add(getMessage(msgID, tmpBackingFile));
            configIsAcceptable = false;
          }
          else
          else if (! p.exists())
          {
            File p = f.getParentFile();
            if (p == null)
            {
              int msgID = MSGID_TASKBE_INVALID_BACKING_FILE_PATH;
              unacceptableReasons.add(getMessage(msgID, tmpBackingFile));
              configIsAcceptable = false;
            }
            else if (! p.exists())
            {
              int msgID = MSGID_TASKBE_BACKING_FILE_MISSING_PARENT;
              unacceptableReasons.add(getMessage(msgID, p.getPath(),
                                                 tmpBackingFile));
              configIsAcceptable = false;
            }
            else if (! p.isDirectory())
            {
              int msgID = MSGID_TASKBE_BACKING_FILE_PARENT_NOT_DIRECTORY;
              unacceptableReasons.add(getMessage(msgID, p.getPath(),
                                                 tmpBackingFile));
              configIsAcceptable = false;
            }
            int msgID = MSGID_TASKBE_BACKING_FILE_MISSING_PARENT;
            unacceptableReasons.add(getMessage(msgID, p.getPath(),
                                               tmpBackingFile));
            configIsAcceptable = false;
          }
          else if (! p.isDirectory())
          {
            int msgID = MSGID_TASKBE_BACKING_FILE_PARENT_NOT_DIRECTORY;
            unacceptableReasons.add(getMessage(msgID, p.getPath(),
                                               tmpBackingFile));
            configIsAcceptable = false;
          }
        }
      }
@@ -1342,36 +1133,6 @@
    }
    description = getMessage(MSGID_TASKBE_DESCRIPTION_RETENTION_TIME);
    IntegerWithUnitConfigAttribute retentionStub =
         new IntegerWithUnitConfigAttribute(ATTR_TASK_RETENTION_TIME,
                                            description, false, timeUnits,
                                            true, 0, false, 0);
    try
    {
      IntegerWithUnitConfigAttribute retentionAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(retentionStub);
      if (retentionAttr == null)
      {
        int msgID = MSGID_TASKBE_NO_RETENTION_TIME;
        unacceptableReasons.add(getMessage(msgID, ATTR_TASK_RETENTION_TIME));
        configIsAcceptable = false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_TASKBE_ERROR_GETTING_RETENTION_TIME;
      unacceptableReasons.add(getMessage(msgID, ATTR_TASK_RETENTION_TIME,
                                         stackTraceToSingleLineString(e)));
      configIsAcceptable = false;
    }
    return configIsAcceptable;
@@ -1380,23 +1141,9 @@
  /**
   * Makes a best-effort attempt to apply the configuration contained in the
   * provided entry.  Information about the result of this processing should be
   * added to the provided message list.  Information should always be added to
   * this list if a configuration change could not be applied.  If detailed
   * results are requested, then information about the changes applied
   * successfully (and optionally about parameters that were not changed) should
   * also be included.
   *
   * @param  configEntry      The entry containing the new configuration to
   *                          apply for this component.
   * @param  detailedResults  Indicates whether detailed information about the
   *                          processing should be added to the list.
   *
   * @return  Information about the result of the configuration update.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
                                                  boolean detailedResults)
  public ConfigChangeResult applyConfigurationChange(TaskBackendCfg configEntry)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
@@ -1404,23 +1151,10 @@
    String tmpBackingFile = taskBackingFile;
    String description = getMessage(MSGID_TASKBE_DESCRIPTION_BACKING_FILE);
    StringConfigAttribute backingStub =
         new StringConfigAttribute(ATTR_TASK_BACKING_FILE, description, true,
                                   false, false);
    try
    {
      StringConfigAttribute backingAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(backingStub);
      if (backingAttr == null)
      {
        int msgID = MSGID_TASKBE_NO_BACKING_FILE;
        messages.add(getMessage(msgID, ATTR_TASK_BACKING_FILE));
        resultCode = ResultCode.OBJECTCLASS_VIOLATION;
      }
      else
      {
        tmpBackingFile = backingAttr.pendingValue();
        tmpBackingFile = configEntry.getTaskBackingFile();
        if (! taskBackingFile.equals(tmpBackingFile))
        {
          File f = getFileForPath(tmpBackingFile);
@@ -1470,48 +1204,7 @@
    }
    long tmpRetentionTime = retentionTime;
    description = getMessage(MSGID_TASKBE_DESCRIPTION_RETENTION_TIME);
    IntegerWithUnitConfigAttribute retentionStub =
         new IntegerWithUnitConfigAttribute(ATTR_TASK_RETENTION_TIME,
                                            description, false, timeUnits,
                                            true, 0, false, 0);
    try
    {
      IntegerWithUnitConfigAttribute retentionAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(retentionStub);
      if (retentionAttr == null)
      {
        int msgID = MSGID_TASKBE_NO_RETENTION_TIME;
        messages.add(getMessage(msgID, ATTR_TASK_RETENTION_TIME));
        if (resultCode == ResultCode.SUCCESS)
        {
          resultCode = ResultCode.OBJECTCLASS_VIOLATION;
        }
      }
      else
      {
        tmpRetentionTime = retentionAttr.activeCalculatedValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_TASKBE_ERROR_GETTING_RETENTION_TIME;
      messages.add(getMessage(msgID, ATTR_TASK_RETENTION_TIME,
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    long tmpRetentionTime = configEntry.getTaskRetentionTime();
    if (resultCode == ResultCode.SUCCESS)
@@ -1537,6 +1230,7 @@
    }
    currentConfig = configEntry;
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
@@ -1653,5 +1347,13 @@
  {
    return taskScheduler.getRecurringTask(taskEntryDN);
  }
  private static TaskBackendCfg getTaskBackendCfg(ConfigEntry configEntry)
      throws ConfigException {
    return BackendConfigManager.getConfiguration(
         TaskBackendCfgDefn.getInstance(), configEntry);
  }
}
opends/src/server/org/opends/server/core/BackendConfigManager.java
@@ -31,42 +31,38 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.server.api.Backend;
import org.opends.server.api.BackendInitializationListener;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.api.ConfigChangeListener;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.api.ConfigHandler;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.config.MultiChoiceConfigAttribute;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.WritabilityMode;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigConstants;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.types.DebugLogLevel;
import static org.opends.server.loggers.Error.*;
import org.opends.server.types.*;
import static org.opends.server.loggers.Error.logError;
import static org.opends.server.messages.ConfigMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.server.ServerManagementContext;
import org.opends.server.admin.server.ServerManagedObject;
import org.opends.server.admin.server.ConfigExceptionFactory;
import org.opends.server.admin.server.ServerManagedObjectDecodingException;
import org.opends.server.admin.std.server.BackendCfg;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.admin.std.meta.BackendCfgDefn;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.DefinitionDecodingException;
/**
@@ -76,8 +72,10 @@
 * started, and then will manage any changes to them while the server is
 * running.
 */
public class BackendConfigManager
       implements ConfigChangeListener, ConfigAddListener, ConfigDeleteListener
public class BackendConfigManager implements
     ConfigurationChangeListener<BackendCfg>,
     ConfigurationAddListener<BackendCfg>,
     ConfigurationDeleteListener<BackendCfg>
{
@@ -86,9 +84,6 @@
  // backend implementations.
  private ConcurrentHashMap<DN,Backend> registeredBackends;
  // The DN of the associated configuration entry.
  private DN configEntryDN;
  /**
@@ -118,13 +113,21 @@
    registeredBackends = new ConcurrentHashMap<DN,Backend>();
    // Create an internal server management context and retrieve
    // the root configuration.
    ServerManagementContext context = ServerManagementContext.getInstance();
    RootCfg root = context.getRootConfiguration();
    // Register add and delete listeners.
    root.addBackendAddListener(this);
    root.addBackendDeleteListener(this);
    // Get the configuration entry that is at the root of all the backends in
    // the server.
    ConfigEntry backendRoot;
    try
    {
      configEntryDN = DN.decode(DN_BACKEND_BASE);
      DN configEntryDN = DN.decode(ConfigConstants.DN_BACKEND_BASE);
      backendRoot   = DirectoryServer.getConfigEntry(configEntryDN);
    }
    catch (Exception e)
@@ -137,6 +140,7 @@
      int    msgID   = MSGID_CONFIG_BACKEND_CANNOT_GET_CONFIG_BASE;
      String message = getMessage(msgID, stackTraceToSingleLineString(e));
      throw new ConfigException(msgID, message, e);
    }
@@ -151,701 +155,313 @@
    }
    // Register as an add and delete listener for the base entry so that we can
    // be notified if new backends are added or existing backends are removed.
    backendRoot.registerAddListener(this);
    backendRoot.registerDeleteListener(this);
    // Iterate through the set of immediate children below the backend config
    // root.
    for (ConfigEntry backendEntry : backendRoot.getChildren().values())
    // Initialize existing backends.
    for (String name : root.listBackends())
    {
      DN backendDN = backendEntry.getDN();
      // Get the handler's configuration.
      // This will decode and validate its properties.
      BackendCfg backendCfg = root.getBackend(name);
      DN backendDN = backendCfg.dn();
      String backendID = backendCfg.getBackendId();
      // Register as a change listener for this backend entry so that we will
      // be notified of any changes that may be made to it.
      backendEntry.registerChangeListener(this);
      // Register as a change listener for this backend so that we can be
      // notified when it is disabled or enabled.
      backendCfg.addChangeListener(this);
      // Check to see if this entry appears to contain a backend configuration.
      // If not, log a warning and skip it.
      try
      // Ignore this handler if it is disabled.
      if (backendCfg.isBackendEnabled())
      {
        SearchFilter backendFilter =
             SearchFilter.createFilterFromString("(objectClass=" + OC_BACKEND +
                                                 ")");
        if (! backendFilter.matchesEntry(backendEntry.getEntry()))
        // If there is already a backend registered with the specified ID,
        // then log an error and skip it.
        if (DirectoryServer.hasBackend(backendCfg.getBackendId()))
        {
          int msgID = MSGID_CONFIG_BACKEND_ENTRY_DOES_NOT_HAVE_BACKEND_CONFIG;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          continue;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // See if the entry contains an attribute that indicates whether the
      // backend should be enabled.  If it does not, or if it is not set to
      // "true", then skip it.
      int msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_ENABLED;
      BooleanConfigAttribute enabledStub =
           new BooleanConfigAttribute(ATTR_BACKEND_ENABLED, getMessage(msgID),
                                      false);
      try
      {
        BooleanConfigAttribute enabledAttr =
             (BooleanConfigAttribute)
             backendEntry.getConfigAttribute(enabledStub);
        if (enabledAttr == null)
        {
          // The attribute is not present, so this backend will be disabled.
          // Log a message and continue.
          msgID = MSGID_CONFIG_BACKEND_NO_ENABLED_ATTR;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          continue;
        }
        else if (! enabledAttr.activeValue())
        {
          // The backend is explicitly disabled.  Log a mild warning and
          // continue.
          msgID = MSGID_CONFIG_BACKEND_DISABLED;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.INFORMATIONAL, message, msgID);
          continue;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // See if the entry contains an attribute that specifies the backend ID.
      // If it does not, then log an error and skip it.
      msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BACKEND_ID;
      String backendID = null;
      StringConfigAttribute idStub =
           new StringConfigAttribute(ATTR_BACKEND_ID, getMessage(msgID),
                                     true, false, true);
      try
      {
        StringConfigAttribute idAttr =
             (StringConfigAttribute) backendEntry.getConfigAttribute(idStub);
        if (idAttr == null)
        {
          msgID = MSGID_CONFIG_BACKEND_NO_BACKEND_ID;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          continue;
        }
        else
        {
          backendID = idAttr.activeValue();
          // If there is already a backend registered with the specified ID,
          // then log an error and skip it.
          if (DirectoryServer.hasBackend(backendID))
          {
            msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
            String message = getMessage(msgID, backendID,
                                        String.valueOf(backendDN));
            logError(ErrorLogCategory.CONFIGURATION,
                     ErrorLogSeverity.SEVERE_WARNING, message, msgID);
            continue;
          }
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BACKEND_ID;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // Get the writability mode for this backend.  It must be provided.
      LinkedHashSet<String> writabilityModes = new LinkedHashSet<String>(3);
      writabilityModes.add(WritabilityMode.ENABLED.toString());
      writabilityModes.add(WritabilityMode.DISABLED.toString());
      writabilityModes.add(WritabilityMode.INTERNAL_ONLY.toString());
      msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_WRITABILITY;
      WritabilityMode writabilityMode = null;
      MultiChoiceConfigAttribute writabilityStub =
           new MultiChoiceConfigAttribute(ATTR_BACKEND_WRITABILITY_MODE,
                                          getMessage(msgID), true, false, false,
                                          writabilityModes);
      try
      {
        MultiChoiceConfigAttribute writabilityAttr =
             (MultiChoiceConfigAttribute)
             backendEntry.getConfigAttribute(writabilityStub);
        if (writabilityAttr == null)
        {
          msgID = MSGID_CONFIG_BACKEND_NO_WRITABILITY_MODE;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR, message, msgID);
          continue;
        }
        else
        {
          writabilityMode =
               WritabilityMode.modeForName(writabilityAttr.activeValue());
          if (writabilityMode == null)
          {
            msgID = MSGID_CONFIG_BACKEND_INVALID_WRITABILITY_MODE;
            String message =
                 getMessage(msgID, String.valueOf(backendDN),
                            String.valueOf(writabilityAttr.activeValue()));
            logError(ErrorLogCategory.CONFIGURATION,
                     ErrorLogSeverity.SEVERE_ERROR, message, msgID);
            continue;
          }
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_WRITABILITY;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // See if the entry contains an attribute that specifies the base DNs for
      // the backend.  If it does not, then log an error and skip it.
      msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
      DN[] baseDNs = null;
      DNConfigAttribute baseDNStub =
           new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID),
                                 true, true, true);
      try
      {
        DNConfigAttribute baseDNAttr =
             (DNConfigAttribute) backendEntry.getConfigAttribute(baseDNStub);
        if (baseDNAttr == null)
        {
          msgID = MSGID_CONFIG_BACKEND_NO_BASE_DNS;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR, message, msgID);
          continue;
        }
        else
        {
          List<DN> dnList = baseDNAttr.activeValues();
          baseDNs = new DN[dnList.size()];
          dnList.toArray(baseDNs);
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BASE_DNS;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // See if the entry contains an attribute that specifies the class name
      // for the backend implementation.  If it does, then load it and make sure
      // that it's a valid backend implementation.  There is no such attribute,
      // the specified class cannot be loaded, or it does not contain a valid
      // backend implementation, then log an error and skip it.
      String className;
      msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_CLASS;
      StringConfigAttribute classStub =
           new StringConfigAttribute(ATTR_BACKEND_CLASS, getMessage(msgID),
                                     true, false, true);
      try
      {
        StringConfigAttribute classAttr =
             (StringConfigAttribute) backendEntry.getConfigAttribute(classStub);
        if (classAttr == null)
        {
          msgID = MSGID_CONFIG_BACKEND_NO_CLASS_ATTR;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR, message, msgID);
          continue;
        }
        else
        {
          className = classAttr.activeValue();
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_GET_CLASS;
        String message = getMessage(msgID, String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      Backend backend;
      try
      {
        Class backendClass = DirectoryServer.loadClass(className);
        backend = (Backend) backendClass.newInstance();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
        String message = getMessage(msgID, String.valueOf(className),
                                    String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        continue;
      }
      // If this backend is a configuration manager, then we don't want to do
      // any more with it because the configuration will have already been
      // started.
      if (backend instanceof ConfigHandler)
      {
        continue;
      }
      // Set the backend ID and writability mode for this backend.
      backend.setBackendID(backendID);
      backend.setWritabilityMode(writabilityMode);
      // Acquire a shared lock on this backend.  This will prevent operations
      // like LDIF import or restore from occurring while the backend is active.
      try
      {
        String lockFile = LockFileManager.getBackendLockFileName(backend);
        StringBuilder failureReason = new StringBuilder();
        if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
        {
          msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
          int msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
          String message = getMessage(msgID, backendID,
                                      String.valueOf(failureReason));
                                      String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          // FIXME -- Do we need to send an admin alert?
          continue;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        // See if the entry contains an attribute that specifies the class name
        // for the backend implementation.  If it does, then load it and make
        // sure that it's a valid backend implementation.  There is no such
        // attribute, the specified class cannot be loaded, or it does not
        // contain a valid backend implementation, then log an error and skip
        // it.
        String className = backendCfg.getBackendClass();
        Class backendClass;
        Backend backend;
        try
        {
          debugCaught(DebugLogLevel.ERROR, e);
          backendClass = DirectoryServer.loadClass(className);
          backend = (Backend) backendClass.newInstance();
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
          String message = getMessage(msgID, String.valueOf(className),
                                      String.valueOf(backendDN),
                                      stackTraceToSingleLineString(e));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR,
                   message, msgID);
          continue;
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
        String message = getMessage(msgID, backendID,
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.SEVERE_WARNING, message, msgID);
        // FIXME -- Do we need to send an admin alert?
        continue;
      }
      // Perform the necessary initialization for the backend entry.
      try
      {
        backend.initializeBackend(backendEntry, baseDNs);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        // If this backend is a configuration manager, then we don't want to do
        // any more with it because the configuration will have already been
        // started.
        if (backend instanceof ConfigHandler)
        {
          debugCaught(DebugLogLevel.ERROR, e);
          continue;
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
        String message = getMessage(msgID, String.valueOf(className),
                                    String.valueOf(backendDN),
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        // See if the entry contains an attribute that specifies the writability
        // mode.
        WritabilityMode writabilityMode = WritabilityMode.ENABLED;
        BackendCfgDefn.BackendWritabilityMode bwm =
             backendCfg.getBackendWritabilityMode();
        switch (bwm)
        {
          case DISABLED:
            writabilityMode = WritabilityMode.DISABLED;
            break;
          case ENABLED:
            writabilityMode = WritabilityMode.ENABLED;
            break;
          case INTERNAL_ONLY:
            writabilityMode = WritabilityMode.INTERNAL_ONLY;
            break;
        }
        // Set the backend ID and writability mode for this backend.
        backend.setBackendID(backendID);
        backend.setWritabilityMode(writabilityMode);
        // Acquire a shared lock on this backend.  This will prevent operations
        // like LDIF import or restore from occurring while the backend is
        // active.
        try
        {
          String lockFile = LockFileManager.getBackendLockFileName(backend);
          StringBuilder failureReason = new StringBuilder();
          if (! LockFileManager.releaseLock(lockFile, failureReason))
          if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
          {
            int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
            String message = getMessage(msgID, backendID,
                                        String.valueOf(failureReason));
            logError(ErrorLogCategory.CONFIGURATION,
                     ErrorLogSeverity.SEVERE_WARNING, message, msgID);
            // FIXME -- Do we need to send an admin alert?
            continue;
          }
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
          String message = getMessage(msgID, backendID,
                                      stackTraceToSingleLineString(e));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          // FIXME -- Do we need to send an admin alert?
          continue;
        }
        // Perform the necessary initialization for the backend entry.
        DN[] baseDNs = new DN[backendCfg.getBackendBaseDN().size()];
        baseDNs = backendCfg.getBackendBaseDN().toArray(baseDNs);
        try
        {
          backend.initializeBackend(
               DirectoryServer.getConfigEntry(backendCfg.dn()), baseDNs);
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
          String message = getMessage(msgID, String.valueOf(className),
                                      String.valueOf(backendDN),
                                      stackTraceToSingleLineString(e));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR,
                   message, msgID);
          try
          {
            String lockFile = LockFileManager.getBackendLockFileName(backend);
            StringBuilder failureReason = new StringBuilder();
            if (! LockFileManager.releaseLock(lockFile, failureReason))
            {
              msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
              message = getMessage(msgID, backendID,
                                   String.valueOf(failureReason));
              logError(ErrorLogCategory.CONFIGURATION,
                       ErrorLogSeverity.SEVERE_WARNING, message, msgID);
              // FIXME -- Do we need to send an admin alert?
            }
          }
          catch (Exception e2)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, e2);
            }
            msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
            message = getMessage(msgID, backendID,
                                 String.valueOf(failureReason));
                                 stackTraceToSingleLineString(e2));
            logError(ErrorLogCategory.CONFIGURATION,
                     ErrorLogSeverity.SEVERE_WARNING, message, msgID);
            // FIXME -- Do we need to send an admin alert?
          }
          continue;
        }
        catch (Exception e2)
        // Notify any backend initialization listeners.
        for (BackendInitializationListener listener :
             DirectoryServer.getBackendInitializationListeners())
        {
          listener.performBackendInitializationProcessing(backend);
        }
        // Register the backend with the server.
        try
        {
          DirectoryServer.registerBackend(backend);
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e2);
            debugCaught(DebugLogLevel.ERROR, e);
          }
          msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
          message = getMessage(msgID, backendID,
                               stackTraceToSingleLineString(e2));
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
          String message = getMessage(msgID, backendID,
                                      stackTraceToSingleLineString(e));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
                   ErrorLogSeverity.SEVERE_ERROR,
                   message, msgID);
          // FIXME -- Do we need to send an admin alert?
        }
        continue;
        // Put this backend in the hash so that we will be able to find it if it
        // is altered.
        registeredBackends.put(backendDN, backend);
      }
      // Notify any backend initialization listeners.
      for (BackendInitializationListener listener :
           DirectoryServer.getBackendInitializationListeners())
      else
      {
        listener.performBackendInitializationProcessing(backend);
        // The backend is explicitly disabled.  Log a mild warning and
        // continue.
        int msgID = MSGID_CONFIG_BACKEND_DISABLED;
        String message = getMessage(msgID, String.valueOf(backendDN));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.INFORMATIONAL, message, msgID);
      }
      // Register the backend with the server.
      try
      {
        DirectoryServer.registerBackend(backend);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
        String message = getMessage(msgID, backendID,
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
                 message, msgID);
        // FIXME -- Do we need to send an admin alert?
      }
      // Put this backend in the hash so that we will be able to find it if it
      // is altered.
      registeredBackends.put(backendDN, backend);
    }
  }
  /**
   * Indicates whether the configuration entry that will result from a proposed
   * modification is acceptable to this change listener.
   *
   * @param  configEntry         The configuration entry that will result from
   *                             the requested update.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed change is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry contains an acceptable
   *          configuration, or <CODE>false</CODE> if it does not.
   * {@inheritDoc}
   */
  public boolean configChangeIsAcceptable(ConfigEntry configEntry,
                                          StringBuilder unacceptableReason)
  public boolean isConfigurationChangeAcceptable(
       BackendCfg configEntry,
       List<String> unacceptableReason)
  {
    DN backendDN = configEntry.getDN();
    DN backendDN = configEntry.dn();
    // Check to see if this entry appears to contain a backend configuration.
    // If not, log a warning and skip it.
    try
    Set<DN> baseDNs = configEntry.getBackendBaseDN();
    // See if the backend is registered with the server.  If it is, then
    // see what's changed and whether those changes are acceptable.
    Backend backend = registeredBackends.get(backendDN);
    if (backend != null)
    {
      SearchFilter backendFilter =
           SearchFilter.createFilterFromString("(objectClass=" + OC_BACKEND +
                                               ")");
      if (! backendFilter.matchesEntry(configEntry.getEntry()))
      LinkedHashSet<DN> removedDNs = new LinkedHashSet<DN>();
      for (DN dn : backend.getBaseDNs())
      {
        int msgID = MSGID_CONFIG_BACKEND_ENTRY_DOES_NOT_HAVE_BACKEND_CONFIG;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
        removedDNs.add(dn);
      }
      int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that indicates whether the
    // backend should be enabled.  If it does not, or if it is not set to
    // "true", then skip it.
    int msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_ENABLED;
    BooleanConfigAttribute enabledStub =
         new BooleanConfigAttribute(ATTR_BACKEND_ENABLED, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute enabledAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      LinkedHashSet<DN> addedDNs = new LinkedHashSet<DN>();
      for (DN dn : baseDNs)
      {
        // The attribute is not present, so this backend will be disabled.
        msgID = MSGID_CONFIG_BACKEND_NO_ENABLED_ATTR;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
        addedDNs.add(dn);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that specifies the backend ID.  If
    // it does not, then reject it.
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BACKEND_ID;
    StringConfigAttribute idStub =
         new StringConfigAttribute(ATTR_BACKEND_ID, getMessage(msgID), true,
                                   false, true);
    try
    {
      StringConfigAttribute idAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(idStub);
      if (idAttr == null)
      Iterator<DN> iterator = removedDNs.iterator();
      while (iterator.hasNext())
      {
        // The attribute is not present.  We will not allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BACKEND_ID;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BACKEND_ID;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that specifies the writability
    // mode.  If it does not, then reject it.
    LinkedHashSet<String> writabilityModes = new LinkedHashSet<String>(3);
    writabilityModes.add(WritabilityMode.ENABLED.toString());
    writabilityModes.add(WritabilityMode.DISABLED.toString());
    writabilityModes.add(WritabilityMode.INTERNAL_ONLY.toString());
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_WRITABILITY;
    MultiChoiceConfigAttribute writabilityStub =
         new MultiChoiceConfigAttribute(ATTR_BACKEND_WRITABILITY_MODE,
                                        getMessage(msgID), true, false, false,
                                        writabilityModes);
    try
    {
      MultiChoiceConfigAttribute writabilityAttr =
           (MultiChoiceConfigAttribute)
           configEntry.getConfigAttribute(writabilityStub);
      if (writabilityAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_WRITABILITY_MODE;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_WRITABILITY;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that specifies the set of base DNs
    // for the backend.  If it does not, then skip it.
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
    DNConfigAttribute baseDNStub =
         new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID), true,
                               true, true);
    try
    {
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      if (baseDNAttr == null)
      {
        // The attribute is not present.  We will not allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BASE_DNS;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
      // See if the backend is registered with the server.  If it is, then
      // see what's changed and whether those changes are acceptable.
      Backend backend = registeredBackends.get(configEntryDN);
      if (backend != null)
      {
        LinkedHashSet<DN> removedDNs = new LinkedHashSet<DN>();
        for (DN dn : backend.getBaseDNs())
        DN dn = iterator.next();
        if (addedDNs.remove(dn))
        {
          removedDNs.add(dn);
        }
        LinkedHashSet<DN> addedDNs = new LinkedHashSet<DN>();
        for (DN dn : baseDNAttr.pendingValues())
        {
          addedDNs.add(dn);
        }
        Iterator<DN> iterator = removedDNs.iterator();
        while (iterator.hasNext())
        {
          DN dn = iterator.next();
          if (addedDNs.remove(dn))
          {
            iterator.remove();
          }
        }
        for (DN dn : addedDNs)
        {
          try
          {
            DirectoryServer.registerBaseDN(dn, backend, false, true);
          }
          catch (DirectoryException de)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, de);
            }
            unacceptableReason.append(de.getMessage());
            return false;
          }
        }
        for (DN dn : removedDNs)
        {
          try
          {
            DirectoryServer.deregisterBaseDN(dn, true);
          }
          catch (DirectoryException de)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, de);
            }
            unacceptableReason.append(de.getMessage());
            return false;
          }
          iterator.remove();
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      for (DN dn : addedDNs)
      {
        debugCaught(DebugLogLevel.ERROR, e);
        try
        {
          DirectoryServer.registerBaseDN(dn, backend, false, true);
        }
        catch (DirectoryException de)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, de);
          }
          unacceptableReason.add(de.getMessage());
          return false;
        }
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BASE_DNS;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                stackTraceToSingleLineString(e)));
      return false;
      for (DN dn : removedDNs)
      {
        try
        {
          DirectoryServer.deregisterBaseDN(dn, true);
        }
        catch (DirectoryException de)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, de);
          }
          unacceptableReason.add(de.getMessage());
          return false;
        }
      }
    }
@@ -854,47 +470,15 @@
    // that it's a valid backend implementation.  There is no such attribute,
    // the specified class cannot be loaded, or it does not contain a valid
    // backend implementation, then log an error and skip it.
    String className;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_CLASS;
    StringConfigAttribute classStub =
         new StringConfigAttribute(ATTR_BACKEND_CLASS, getMessage(msgID),
                                   true, false, true);
    try
    {
      StringConfigAttribute classAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_CLASS_ATTR;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
      else
      {
        className = classAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_GET_CLASS;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    String className = configEntry.getBackendClass();
    try
    {
      Class backendClass = DirectoryServer.loadClass(className);
      if (! Backend.class.isAssignableFrom(backendClass))
      {
        msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
        unacceptableReason.append(getMessage(msgID, String.valueOf(className),
                                             String.valueOf(backendDN)));
        int msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
        unacceptableReason.add(getMessage(msgID, String.valueOf(className),
                                          String.valueOf(backendDN)));
        return false;
      }
    }
@@ -905,10 +489,10 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      unacceptableReason.append(getMessage(msgID, String.valueOf(className),
                                           String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      unacceptableReason.add(getMessage(msgID, String.valueOf(className),
                                        String.valueOf(backendDN),
                                        stackTraceToSingleLineString(e)));
      return false;
    }
@@ -920,79 +504,24 @@
  }
  /**
   * Attempts to apply a new configuration to this Directory Server component
   * based on the provided changed entry.
   *
   * @param  configEntry  The configuration entry that containing the updated
   *                      configuration for this component.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry)
  public ConfigChangeResult applyConfigurationChange(BackendCfg cfg)
  {
    DN                backendDN           = configEntry.getDN();
    DN                backendDN           = cfg.dn();
    Backend           backend             = registeredBackends.get(backendDN);
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    // Check to see if this entry appears to contain a backend configuration.
    // If not, log a warning and skip it.
    try
    {
      SearchFilter backendFilter =
           SearchFilter.createFilterFromString("(objectClass=" + OC_BACKEND +
                                               ")");
      if (! backendFilter.matchesEntry(configEntry.getEntry()))
      {
        int msgID = MSGID_CONFIG_BACKEND_ENTRY_DOES_NOT_HAVE_BACKEND_CONFIG;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // See if the entry contains an attribute that indicates whether the
    // backend should be enabled.
    boolean needToEnable = false;
    int msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_ENABLED;
    BooleanConfigAttribute enabledStub =
         new BooleanConfigAttribute(ATTR_BACKEND_ENABLED, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute enabledAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        // The attribute is not present.  We won't allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_ENABLED_ATTR;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else if (enabledAttr.pendingValue())
      if (cfg.isBackendEnabled())
      {
        // The backend is marked as enabled.  See if that is already true.
        if (backend == null)
@@ -1029,7 +558,7 @@
            StringBuilder failureReason = new StringBuilder();
            if (! LockFileManager.releaseLock(lockFile, failureReason))
            {
              msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
              int msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
              String message = getMessage(msgID, backend.getBackendID(),
                                          String.valueOf(failureReason));
              logError(ErrorLogCategory.CONFIGURATION,
@@ -1044,7 +573,7 @@
              debugCaught(DebugLogLevel.ERROR, e2);
            }
            msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
            int msgID = MSGID_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK;
            String message = getMessage(msgID, backend.getBackendID(),
                                        stackTraceToSingleLineString(e2));
            logError(ErrorLogCategory.CONFIGURATION,
@@ -1068,7 +597,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
      int msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
@@ -1078,141 +607,32 @@
    // See if the entry contains an attribute that specifies the backend ID for
    // the backend.
    String backendID = null;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BACKEND_ID;
    StringConfigAttribute idStub =
         new StringConfigAttribute(ATTR_BACKEND_ID, getMessage(msgID), true,
                                   false, true);
    try
    {
      StringConfigAttribute idAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(idStub);
      if (idAttr == null)
      {
        // The attribute is not present.  We won't allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BACKEND_ID;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        backendID = idAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BACKEND_ID;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    String backendID = cfg.getBackendId();
    // See if the entry contains an attribute that specifies the writability
    // mode.
    LinkedHashSet<String> writabilityModes = new LinkedHashSet<String>(3);
    writabilityModes.add(WritabilityMode.ENABLED.toString());
    writabilityModes.add(WritabilityMode.DISABLED.toString());
    writabilityModes.add(WritabilityMode.INTERNAL_ONLY.toString());
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_WRITABILITY;
    WritabilityMode writabilityMode = null;
    MultiChoiceConfigAttribute writabilityStub =
         new MultiChoiceConfigAttribute(ATTR_BACKEND_WRITABILITY_MODE,
                                        getMessage(msgID), true, false, false,
                                        writabilityModes);
    try
    WritabilityMode writabilityMode = WritabilityMode.ENABLED;
    BackendCfgDefn.BackendWritabilityMode bwm =
         cfg.getBackendWritabilityMode();
    switch (bwm)
    {
      MultiChoiceConfigAttribute writabilityAttr =
           (MultiChoiceConfigAttribute)
           configEntry.getConfigAttribute(writabilityStub);
      if (writabilityStub == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_WRITABILITY_MODE;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        writabilityMode =
             WritabilityMode.modeForName(writabilityAttr.activeValue());
        if (writabilityMode == null)
        {
          msgID = MSGID_CONFIG_BACKEND_INVALID_WRITABILITY_MODE;
          messages.add(getMessage(msgID, String.valueOf(backendDN),
                            String.valueOf(writabilityAttr.activeValue())));
          resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
          return new ConfigChangeResult(resultCode, adminActionRequired,
                                        messages);
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_WRITABILITY;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
      case DISABLED:
        writabilityMode = WritabilityMode.DISABLED;
        break;
      case ENABLED:
        writabilityMode = WritabilityMode.ENABLED;
        break;
      case INTERNAL_ONLY:
        writabilityMode = WritabilityMode.INTERNAL_ONLY;
        break;
    }
    // See if the entry contains an attribute that specifies the base DNs for
    // the backend.
    DN[] baseDNs = null;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
    DNConfigAttribute baseDNStub =
         new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID), true,
                               true, true);
    try
    {
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      if (baseDNAttr == null)
      {
        // The attribute is not present.  We won't allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BASE_DNS;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        List<DN> baseList = baseDNAttr.pendingValues();
        baseDNs = new DN[baseList.size()];
        baseList.toArray(baseDNs);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BASE_DNS;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    Set<DN> baseList = cfg.getBackendBaseDN();
    DN[] baseDNs = new DN[baseList.size()];
    baseList.toArray(baseDNs);
    // See if the entry contains an attribute that specifies the class name
@@ -1220,42 +640,7 @@
    // that it's a valid backend implementation.  There is no such attribute,
    // the specified class cannot be loaded, or it does not contain a valid
    // backend implementation, then log an error and skip it.
    String className;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_CLASS;
    StringConfigAttribute classStub =
         new StringConfigAttribute(ATTR_BACKEND_CLASS, getMessage(msgID),
                                   true, false, true);
    try
    {
      StringConfigAttribute classAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_CLASS_ATTR;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        className = classAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_GET_CLASS;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired,
                                    messages);
    }
    String className = cfg.getBackendClass();
    // See if this backend is currently active and if so if the name of the
@@ -1274,7 +659,7 @@
            // It appears to be a valid backend class.  We'll return that the
            // change is successful, but indicate that some administrative
            // action is required.
            msgID = MSGID_CONFIG_BACKEND_ACTION_REQUIRED_TO_CHANGE_CLASS;
            int msgID = MSGID_CONFIG_BACKEND_ACTION_REQUIRED_TO_CHANGE_CLASS;
            messages.add(getMessage(msgID, String.valueOf(backendDN),
                                    backend.getClass().getName(), className));
            adminActionRequired = true;
@@ -1284,7 +669,7 @@
          else
          {
            // It is not a valid backend class.  This is an error.
            msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
            int msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
            messages.add(getMessage(msgID, String.valueOf(className),
                                    String.valueOf(backendDN)));
            resultCode = ResultCode.CONSTRAINT_VIOLATION;
@@ -1299,7 +684,7 @@
            debugCaught(DebugLogLevel.ERROR, e);
          }
          msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
          messages.add(getMessage(msgID, String.valueOf(className),
                                  String.valueOf(backendDN),
                                  stackTraceToSingleLineString(e)));
@@ -1315,15 +700,16 @@
    // backend.  Try to do so.
    if (needToEnable)
    {
      Class backendClass;
      try
      {
        Class backendClass = DirectoryServer.loadClass(className);
        backendClass = DirectoryServer.loadClass(className);
        backend = (Backend) backendClass.newInstance();
      }
      catch (Exception e)
      {
        // It is not a valid backend class.  This is an error.
        msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
        int msgID = MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND;
        messages.add(getMessage(msgID, String.valueOf(className),
                                String.valueOf(backendDN)));
        resultCode = ResultCode.CONSTRAINT_VIOLATION;
@@ -1345,7 +731,7 @@
        StringBuilder failureReason = new StringBuilder();
        if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
        {
          msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
          int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
          String message = getMessage(msgID, backendID,
                                      String.valueOf(failureReason));
          logError(ErrorLogCategory.CONFIGURATION,
@@ -1366,7 +752,7 @@
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
        int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
        String message = getMessage(msgID, backendID,
                                    stackTraceToSingleLineString(e));
        logError(ErrorLogCategory.CONFIGURATION,
@@ -1383,7 +769,8 @@
      try
      {
        backend.initializeBackend(configEntry, baseDNs);
        backend.initializeBackend(
             DirectoryServer.getConfigEntry(cfg.dn()), baseDNs);
      }
      catch (Exception e)
      {
@@ -1392,7 +779,7 @@
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
        int msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
        messages.add(getMessage(msgID, String.valueOf(className),
                                String.valueOf(backendDN),
                                stackTraceToSingleLineString(e)));
@@ -1452,7 +839,7 @@
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
        int msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
        String message = getMessage(msgID, backendID,
                                    stackTraceToSingleLineString(e));
@@ -1486,203 +873,33 @@
  }
  /**
   * Indicates whether the configuration entry that will result from a proposed
   * add is acceptable to this add listener.
   *
   * @param  configEntry         The configuration entry that will result from
   *                             the requested add.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed entry is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry contains an acceptable
   *          configuration, or <CODE>false</CODE> if it does not.
   * {@inheritDoc}
   */
  public boolean configAddIsAcceptable(ConfigEntry configEntry,
                                       StringBuilder unacceptableReason)
  public boolean isConfigurationAddAcceptable(
       BackendCfg configEntry,
       List<String> unacceptableReason)
  {
    DN backendDN = configEntry.getDN();
    // Check to see if this entry appears to contain a backend configuration.
    // If not then fail.
    try
    {
      SearchFilter backendFilter =
           SearchFilter.createFilterFromString("(objectClass=" + OC_BACKEND +
                                               ")");
      if (! backendFilter.matchesEntry(configEntry.getEntry()))
      {
        int msgID = MSGID_CONFIG_BACKEND_ENTRY_DOES_NOT_HAVE_BACKEND_CONFIG;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that indicates whether the
    // backend should be enabled.  If it does not, or if it is not set to
    // "true", then skip it.
    int msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_ENABLED;
    BooleanConfigAttribute enabledStub =
         new BooleanConfigAttribute(ATTR_BACKEND_ENABLED, getMessage(msgID),
                                    false);
    try
    {
      BooleanConfigAttribute enabledAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        // The attribute is not present.  We will not allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_ENABLED_ATTR;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    DN backendDN = configEntry.dn();
    // See if the entry contains an attribute that specifies the backend ID.  If
    // it does not, then skip it.
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BACKEND_ID;
    StringConfigAttribute idStub =
         new StringConfigAttribute(ATTR_BACKEND_ID, getMessage(msgID), true,
                                   false, true);
    try
    String backendID = configEntry.getBackendId();
    if (DirectoryServer.hasBackend(backendID))
    {
      StringConfigAttribute idAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(idStub);
      if (idAttr == null)
      {
        // The attribute is not present.  We will not allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BACKEND_ID;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
      else
      {
        String backendID = idAttr.activeValue();
        if (DirectoryServer.hasBackend(backendID))
        {
          msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
          unacceptableReason.append(getMessage(msgID,
                                               String.valueOf(backendDN)));
          return false;
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BACKEND_ID;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that specifies the writability
    // mode.  If it does not, then reject it.
    LinkedHashSet<String> writabilityModes = new LinkedHashSet<String>(3);
    writabilityModes.add(WritabilityMode.ENABLED.toString());
    writabilityModes.add(WritabilityMode.DISABLED.toString());
    writabilityModes.add(WritabilityMode.INTERNAL_ONLY.toString());
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_WRITABILITY;
    MultiChoiceConfigAttribute writabilityStub =
         new MultiChoiceConfigAttribute(ATTR_BACKEND_WRITABILITY_MODE,
                                        getMessage(msgID), true, false, false,
                                        writabilityModes);
    try
    {
      MultiChoiceConfigAttribute writabilityAttr =
           (MultiChoiceConfigAttribute)
           configEntry.getConfigAttribute(writabilityStub);
      if (writabilityAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_WRITABILITY_MODE;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_WRITABILITY;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      int msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
      unacceptableReason.add(getMessage(msgID,
                                        String.valueOf(backendDN)));
      return false;
    }
    // See if the entry contains an attribute that specifies the set of base DNs
    // for the backend.  If it does not, then skip it.
    List<DN> baseDNs = null;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
    DNConfigAttribute baseDNStub =
         new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID), true,
                               true, true);
    try
    {
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      if (baseDNAttr == null)
      {
        // The attribute is not present.  We will not allow this.
        msgID = MSGID_CONFIG_BACKEND_NO_BASE_DNS;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
      else
      {
        baseDNs = baseDNAttr.pendingValues();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BASE_DNS;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                stackTraceToSingleLineString(e)));
      return false;
    }
    Set<DN> baseList = configEntry.getBackendBaseDN();
    DN[] baseDNs = new DN[baseList.size()];
    baseList.toArray(baseDNs);
    // See if the entry contains an attribute that specifies the class name
@@ -1690,38 +907,7 @@
    // that it's a valid backend implementation.  There is no such attribute,
    // the specified class cannot be loaded, or it does not contain a valid
    // backend implementation, then log an error and skip it.
    String className;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_CLASS;
    StringConfigAttribute classStub =
         new StringConfigAttribute(ATTR_BACKEND_CLASS, getMessage(msgID),
                                   true, false, true);
    try
    {
      StringConfigAttribute classAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_CLASS_ATTR;
        unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
        return false;
      }
      else
      {
        className = classAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_GET_CLASS;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    String className = configEntry.getBackendClass();
    Backend backend;
    try
@@ -1736,42 +922,42 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      unacceptableReason.append(getMessage(msgID, String.valueOf(className),
                                           String.valueOf(backendDN),
                                           stackTraceToSingleLineString(e)));
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      unacceptableReason.add(getMessage(msgID, String.valueOf(className),
                                        String.valueOf(backendDN),
                                        stackTraceToSingleLineString(e)));
      return false;
    }
    // If the backend is a configurable component, then make sure that its
    // configuration is valid.
    if (backend instanceof ConfigurableComponent)
    {
      ConfigurableComponent cc = (ConfigurableComponent) backend;
      LinkedList<String> errorMessages = new LinkedList<String>();
      if (! cc.hasAcceptableConfiguration(configEntry, errorMessages))
      {
        if (errorMessages.isEmpty())
        {
          msgID = MSGID_CONFIG_BACKEND_UNACCEPTABLE_CONFIG;
          unacceptableReason.append(getMessage(msgID,
                                               String.valueOf(configEntryDN)));
        }
        else
        {
          Iterator<String> iterator = errorMessages.iterator();
          unacceptableReason.append(iterator.next());
          while (iterator.hasNext())
          {
            unacceptableReason.append("  ");
            unacceptableReason.append(iterator.next());
          }
        }
        return false;
      }
    }
//    if (backend instanceof ConfigurableComponent)
//    {
//      ConfigurableComponent cc = (ConfigurableComponent) backend;
//      LinkedList<String> errorMessages = new LinkedList<String>();
//      if (! cc.hasAcceptableConfiguration(configEntry, errorMessages))
//      {
//        if (errorMessages.isEmpty())
//        {
//          int msgID = MSGID_CONFIG_BACKEND_UNACCEPTABLE_CONFIG;
//          unacceptableReason.add(getMessage(msgID,
//                                            String.valueOf(configEntryDN)));
//        }
//        else
//        {
//          Iterator<String> iterator = errorMessages.iterator();
//          unacceptableReason.add(iterator.next());
//          while (iterator.hasNext())
//          {
//            unacceptableReason.add("  ");
//            unacceptableReason.add(iterator.next());
//          }
//        }
//
//        return false;
//      }
//    }
    // Make sure that all of the base DNs are acceptable for use in the server.
@@ -1783,12 +969,12 @@
      }
      catch (DirectoryException de)
      {
        unacceptableReason.append(de.getMessage());
        unacceptableReason.add(de.getMessage());
        return false;
      }
      catch (Exception e)
      {
        unacceptableReason.append(stackTraceToSingleLineString(e));
        unacceptableReason.add(stackTraceToSingleLineString(e));
        return false;
      }
    }
@@ -1803,17 +989,11 @@
  /**
   * Attempts to apply a new configuration based on the provided added entry.
   *
   * @param  configEntry  The new configuration entry that contains the
   *                      configuration to apply.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry)
  public ConfigChangeResult applyConfigurationAdd(BackendCfg cfg)
  {
    DN                backendDN           = configEntry.getDN();
    DN                backendDN           = cfg.dn();
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
@@ -1821,241 +1001,66 @@
    // Register as a change listener for this backend entry so that we will
    // be notified of any changes that may be made to it.
    configEntry.registerChangeListener(this);
    // Check to see if this entry appears to contain a backend configuration.
    // If not then fail.
    try
    {
      SearchFilter backendFilter =
           SearchFilter.createFilterFromString("(objectClass=" + OC_BACKEND +
                                               ")");
      if (! backendFilter.matchesEntry(configEntry.getEntry()))
      {
        int msgID = MSGID_CONFIG_BACKEND_ENTRY_DOES_NOT_HAVE_BACKEND_CONFIG;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    cfg.addChangeListener(this);
    // See if the entry contains an attribute that indicates whether the
    // backend should be enabled.  If it does not, or if it is not set to
    // "true", then skip it.
    int msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_ENABLED;
    BooleanConfigAttribute enabledStub =
         new BooleanConfigAttribute(ATTR_BACKEND_ENABLED, getMessage(msgID),
                                    false);
    try
    if (!cfg.isBackendEnabled())
    {
      BooleanConfigAttribute enabledAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        // The attribute is not present, so this backend will be disabled.  We
        // will log a message to indicate that it won't be enabled and return.
        msgID = MSGID_CONFIG_BACKEND_NO_ENABLED_ATTR;
        String message = getMessage(msgID, String.valueOf(backendDN));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.SEVERE_WARNING, message, msgID);
        messages.add(message);
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else if (! enabledAttr.activeValue())
      {
        // The backend is explicitly disabled.  We will log a message to
        // indicate that it won't be enabled and return.
        msgID = MSGID_CONFIG_BACKEND_DISABLED;
        String message = getMessage(msgID, String.valueOf(backendDN));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.INFORMATIONAL, message, msgID);
        messages.add(message);
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      // The backend is explicitly disabled.  We will log a message to
      // indicate that it won't be enabled and return.
      int msgID = MSGID_CONFIG_BACKEND_DISABLED;
      String message = getMessage(msgID, String.valueOf(backendDN));
      logError(ErrorLogCategory.CONFIGURATION,
               ErrorLogSeverity.INFORMATIONAL, message, msgID);
      messages.add(message);
      return new ConfigChangeResult(resultCode, adminActionRequired,
                                    messages);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // See if the entry contains an attribute that specifies the backend ID.  If
    // it does not, then skip it.
    String backendID;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BACKEND_ID;
    StringConfigAttribute idStub =
         new StringConfigAttribute(ATTR_BACKEND_ID, getMessage(msgID), true,
                                   false, true);
    try
    String backendID = cfg.getBackendId();
    if (DirectoryServer.hasBackend(backendID))
    {
      StringConfigAttribute idAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(idStub);
      if (idAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_BACKEND_ID;
        String message = getMessage(msgID, String.valueOf(backendDN));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.SEVERE_WARNING, message, msgID);
        messages.add(message);
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        backendID = idAttr.pendingValue();
        if (DirectoryServer.hasBackend(backendID))
        {
          msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
          String message = getMessage(msgID, String.valueOf(backendDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          messages.add(message);
          return new ConfigChangeResult(resultCode, adminActionRequired,
                                        messages);
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BACKEND_ID;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
      int msgID = MSGID_CONFIG_BACKEND_DUPLICATE_BACKEND_ID;
      String message = getMessage(msgID, String.valueOf(backendDN));
      logError(ErrorLogCategory.CONFIGURATION,
               ErrorLogSeverity.SEVERE_WARNING, message, msgID);
      messages.add(message);
      return new ConfigChangeResult(resultCode, adminActionRequired,
                                    messages);
    }
    // See if the entry contains an attribute that specifies the writability
    // mode.
    LinkedHashSet<String> writabilityModes = new LinkedHashSet<String>(3);
    writabilityModes.add(WritabilityMode.ENABLED.toString());
    writabilityModes.add(WritabilityMode.DISABLED.toString());
    writabilityModes.add(WritabilityMode.INTERNAL_ONLY.toString());
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_WRITABILITY;
    WritabilityMode writabilityMode = null;
    MultiChoiceConfigAttribute writabilityStub =
         new MultiChoiceConfigAttribute(ATTR_BACKEND_WRITABILITY_MODE,
                                        getMessage(msgID), true, false, false,
                                        writabilityModes);
    try
    WritabilityMode writabilityMode = WritabilityMode.ENABLED;
    BackendCfgDefn.BackendWritabilityMode bwm =
         cfg.getBackendWritabilityMode();
    switch (bwm)
    {
      MultiChoiceConfigAttribute writabilityAttr =
           (MultiChoiceConfigAttribute)
           configEntry.getConfigAttribute(writabilityStub);
      if (writabilityAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_WRITABILITY_MODE;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        writabilityMode =
             WritabilityMode.modeForName(writabilityAttr.activeValue());
        if (writabilityMode == null)
        {
          msgID = MSGID_CONFIG_BACKEND_INVALID_WRITABILITY_MODE;
          messages.add(getMessage(msgID, String.valueOf(backendDN),
                            String.valueOf(writabilityAttr.activeValue())));
          resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
          return new ConfigChangeResult(resultCode, adminActionRequired,
                                        messages);
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_WRITABILITY;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
      case DISABLED:
        writabilityMode = WritabilityMode.DISABLED;
        break;
      case ENABLED:
        writabilityMode = WritabilityMode.ENABLED;
        break;
      case INTERNAL_ONLY:
        writabilityMode = WritabilityMode.INTERNAL_ONLY;
        break;
    }
    // See if the entry contains an attribute that specifies the base DNs for
    // the entry.  If it does not, then skip it.
    DN[] baseDNs = null;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
    DNConfigAttribute baseDNStub =
         new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID),
                               true, true, true);
    try
    {
      DNConfigAttribute baseDNAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub);
      if (baseDNAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_BASE_DNS;
        String message = getMessage(msgID, String.valueOf(backendDN));
        logError(ErrorLogCategory.CONFIGURATION,
                 ErrorLogSeverity.SEVERE_WARNING, message, msgID);
        messages.add(message);
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        List<DN> dnList = baseDNAttr.pendingValues();
        baseDNs = new DN[dnList.size()];
        dnList.toArray(baseDNs);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_UNABLE_TO_DETERMINE_BASE_DNS;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    Set<DN> dnList = cfg.getBackendBaseDN();
    DN[] baseDNs = new DN[dnList.size()];
    dnList.toArray(baseDNs);
    // See if the entry contains an attribute that specifies the class name
@@ -2063,46 +1068,13 @@
    // that it's a valid backend implementation.  There is no such attribute,
    // the specified class cannot be loaded, or it does not contain a valid
    // backend implementation, then log an error and skip it.
    String className;
    msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_CLASS;
    StringConfigAttribute classStub =
         new StringConfigAttribute(ATTR_BACKEND_CLASS, getMessage(msgID),
                                   true, false, true);
    try
    {
      StringConfigAttribute classAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classAttr == null)
      {
        msgID = MSGID_CONFIG_BACKEND_NO_CLASS_ATTR;
        messages.add(getMessage(msgID, String.valueOf(backendDN)));
        resultCode = ResultCode.UNWILLING_TO_PERFORM;
        return new ConfigChangeResult(resultCode, adminActionRequired,
                                      messages);
      }
      else
      {
        className = classAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_GET_CLASS;
      messages.add(getMessage(msgID, String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    String className = cfg.getBackendClass();
    Class backendClass;
    Backend backend;
    try
    {
      Class backendClass = DirectoryServer.loadClass(className);
      backendClass = DirectoryServer.loadClass(className);
      backend = (Backend) backendClass.newInstance();
    }
    catch (Exception e)
@@ -2112,7 +1084,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_INSTANTIATE;
      messages.add(getMessage(msgID, String.valueOf(className),
                              String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
@@ -2135,7 +1107,7 @@
      StringBuilder failureReason = new StringBuilder();
      if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
      {
        msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
        int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
        String message = getMessage(msgID, backendID,
                                    String.valueOf(failureReason));
        logError(ErrorLogCategory.CONFIGURATION,
@@ -2156,7 +1128,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK;
      String message = getMessage(msgID, backendID,
                                  stackTraceToSingleLineString(e));
      logError(ErrorLogCategory.CONFIGURATION,
@@ -2174,7 +1146,8 @@
    // Perform the necessary initialization for the backend entry.
    try
    {
      backend.initializeBackend(configEntry, baseDNs);
      backend.initializeBackend(
           DirectoryServer.getConfigEntry(cfg.dn()), baseDNs);
    }
    catch (Exception e)
    {
@@ -2183,7 +1156,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_INITIALIZE;
      messages.add(getMessage(msgID, String.valueOf(className),
                              String.valueOf(backendDN),
                              stackTraceToSingleLineString(e)));
@@ -2243,7 +1216,7 @@
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
      String message = getMessage(msgID, backendID,
                                  stackTraceToSingleLineString(e));
@@ -2263,24 +1236,14 @@
  }
  /**
   * Indicates whether it is acceptable to remove the provided configuration
   * entry.
   *
   * @param  configEntry         The configuration entry that will be removed
   *                             from the configuration.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed delete is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry may be removed from the
   *          configuration, or <CODE>false</CODE> if not.
   * {@inheritDoc}
   */
  public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
                                          StringBuilder unacceptableReason)
  public boolean isConfigurationDeleteAcceptable(
       BackendCfg configEntry,
       List<String> unacceptableReason)
  {
    DN backendDN = configEntry.getDN();
    DN backendDN = configEntry.dn();
    // See if this backend config manager has a backend registered with the
@@ -2304,24 +1267,18 @@
    else
    {
      int msgID = MSGID_CONFIG_BACKEND_CANNOT_REMOVE_BACKEND_WITH_SUBORDINATES;
      unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
      unacceptableReason.add(getMessage(msgID, String.valueOf(backendDN)));
      return false;
    }
  }
  /**
   * Attempts to apply a new configuration based on the provided deleted entry.
   *
   * @param  configEntry  The new configuration entry that has been deleted.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry)
  public ConfigChangeResult applyConfigurationDelete(BackendCfg configEntry)
  {
    DN                backendDN           = configEntry.getDN();
    DN                backendDN           = configEntry.dn();
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
@@ -2363,7 +1320,7 @@
      }
      DirectoryServer.deregisterBackend(backend);
      configEntry.deregisterChangeListener(this);
      configEntry.removeChangeListener(this);
      // Remove the shared lock for this backend.
      try
@@ -2406,5 +1363,54 @@
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
  }
  /**
   * Gets the configuration corresponding to a config entry.
   *
   * @param <S>
   *          The type of server configuration.
   * @param definition
   *          The required definition of the required managed object.
   * @param configEntry
   *          A configuration entry.
   * @return Returns the server-side configuration.
   * @throws ConfigException
   *           If the entry could not be decoded.
   */
  public static <S extends Configuration> S getConfiguration(
      AbstractManagedObjectDefinition<?, S> definition, ConfigEntry configEntry)
      throws ConfigException {
    try {
      ServerManagedObject<? extends S> mo = ServerManagedObject
          .decode(ManagedObjectPath.emptyPath(), definition,
              configEntry);
      return mo.getConfiguration();
    } catch (DefinitionDecodingException e) {
      throw ConfigExceptionFactory.getInstance()
          .createDecodingExceptionAdaptor(configEntry.getDN(), e);
    } catch (ServerManagedObjectDecodingException e) {
      throw ConfigExceptionFactory.getInstance()
          .createDecodingExceptionAdaptor(e);
    }
  }
  /**
   * Gets the backend configuration corresponding to a backend config entry.
   *
   * @param configEntry A backend config entry.
   * @return Returns the backend configuration.
   * @throws ConfigException If the config entry could not be decoded.
   */
  public static BackendCfg getBackendCfg(ConfigEntry configEntry)
      throws ConfigException
  {
    return getConfiguration(BackendCfgDefn.getInstance(), configEntry);
  }
}
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -4196,8 +4196,11 @@
          baseName = "___NAME___";
        }
        lines.add("  <adm:property name=\"" + baseName + "\" mandatory=\"" +
                  String.valueOf(attr.isRequired()) + "\">");
        lines.add("  <adm:property name=\"" + baseName + "\"");
        lines.add("    mandatory=\""
             + String.valueOf(attr.isRequired()) + "\"");
        lines.add("    multi-valued=\""
             + String.valueOf(attr.isMultiValued()) + "\">");
        lines.add("    <adm:synopsis>");
        lines.add("      ___SYNOPSIS___");
        lines.add("    </adm:synopsis>");
@@ -4238,6 +4241,18 @@
        }
        lines.add("    </adm:description>");
        if (attr.requiresAdminAction())
        {
          lines.add("    <adm:requires-admin-action>");
          lines.add("      <adm:server-restart/>");
          lines.add("    </adm:requires-admin-action>");
        }
        if (!attr.isRequired())
        {
          lines.add("    <adm:default-behavior>");
          lines.add("      <adm:undefined/>");
          lines.add("    </adm:default-behavior>");
        }
        lines.add("    <adm:syntax>");
        if (attr instanceof org.opends.server.config.BooleanConfigAttribute)
opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
@@ -1379,6 +1379,9 @@
    }
    // Save this configuration for future reference.
    currentConfig = config;
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
}
opends/src/server/org/opends/server/messages/AdminMessages.java
@@ -223,7 +223,7 @@
            + "retrieve the managed object configuration entry %s: %s.");
    registerMessage(MSGID_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST,
        "The the managed object configuration entry %s does not "
        "The managed object configuration entry %s does not "
            + "appear to exist in the Directory Server "
            + "configuration. This is a required entry.");
opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -1015,7 +1015,7 @@
   * The message ID for the message that will be used if an attempt is made to
   * normalize the value for an attribute type that does not have an equality
   * matching rule.  This takes two arguments, which are the value to normalize
   * and the the name of the attribute type.
   * and the name of the attribute type.
   */
  public static final int MSGID_ATTR_TYPE_NORMALIZE_NO_MR =
       CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 125;
@@ -6269,7 +6269,7 @@
                    "The Directory Server has completed the configuration " +
                    "bootstrapping process.");
    registerMessage(MSGID_CANNOT_LOAD_CONFIG_HANDLER_CLASS,
                    "Unable to load class %s to serve as the the Directory " +
                    "Unable to load class %s to serve as the Directory " +
                    "Server configuration handler:  %s.");
    registerMessage(MSGID_CANNOT_INSTANTIATE_CONFIG_HANDLER,
                    "Unable to create an instance of class %s to serve as " +
@@ -6727,7 +6727,7 @@
    registerMessage(MSGID_ADD_ASSERTION_FAILED,
                    "Entry %s cannot be added because the request contained " +
                    "an LDAP assertion control and the associated filter did " +
                    "not match the contents of the the provided entry.");
                    "not match the contents of the provided entry.");
    registerMessage(MSGID_ADD_CANNOT_PROCESS_ASSERTION_FILTER,
                    "Entry %s cannot be added because the request contained " +
                    "an LDAP assertion control, but an error occurred while " +
opends/src/server/org/opends/server/messages/JebMessages.java
@@ -63,11 +63,6 @@
  public static final int MSGID_JEB_OPEN_ENV_FAIL =
       CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 3;
/*
  public static final int MSGID_JEB_EMPTY_MESSAGE =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 4;
*/
  /**
   * The message ID of an error indicating that the current highest entry ID
   * in the database could not be determined.  This message takes no arguments.
@@ -132,11 +127,6 @@
  public static final int MSGID_JEB_ENTRY_DATABASE_CORRUPT =
       CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 12;
/*
  public static final int MSGID_JEB_SUFFIXES_NOT_SPECIFIED =
       CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 13;
*/
  /**
   * The message ID of an error indicating that an exception was raised by the
   * JE library while accessing the database.  This message takes one string
@@ -145,11 +135,6 @@
  public static final int MSGID_JEB_DATABASE_EXCEPTION =
       CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 14;
/*
  public static final int MSGID_JEB_JMX_CANNOT_REGISTER_MBEAN =
       CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 15;
*/
  /**
   * The message ID used to describe the attribute which configures
   * the attribute type of an attribute index.
@@ -176,55 +161,6 @@
  /**
   * The message ID used to describe the attribute which configures
   * the database cache size as a percentage of Java VM heap size.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_DATABASE_CACHE_PERCENT =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 19;
  /**
   * The message ID used to describe the attribute which configures
   * the database cache size as an approximate number of bytes.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_DATABASE_CACHE_SIZE =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 20;
  /**
   * The message ID used to describe the attribute which configures
   * whether data updated by a database transaction is forced to disk.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_DATABASE_TXN_NO_SYNC =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 21;
  /**
   * The message ID used to describe the attribute which configures
   * whether data updated by a database transaction is written
   * from the Java VM to the O/S.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_DATABASE_TXN_WRITE_NO_SYNC =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 22;
  /**
   * The message ID used to describe the attribute which configures
   * whether the database background cleaner thread runs.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_DATABASE_RUN_CLEANER =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 23;
  /**
   * The message ID used to describe the attribute which configures
   * the backend entry limit for indexing.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_BACKEND_INDEX_ENTRY_LIMIT =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 24;
  /**
   * The message ID used to describe the attribute which configures
   * the substring length for an attribute index.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_INDEX_SUBSTRING_LENGTH =
@@ -241,11 +177,6 @@
  public static final int MSGID_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE =
       CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 26;
/*
  public static final int MSGID_JEB_CANNOT_ACQUIRE_LOCK =
       CATEGORY_MASK_JEB | SEVERITY_MASK_MILD_ERROR | 27;
*/
  /**
   * The message ID of an error indicating that an unchecked exception was
   * raised during a database transaction.  This message takes no arguments.
@@ -297,21 +228,6 @@
       CATEGORY_MASK_JEB | SEVERITY_MASK_NOTICE | 33;
  /**
   * The message ID used to describe the attribute which configures
   * the minimum percentage of log space that must be used in log files.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_CLEANER_MIN_UTILIZATION =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 34;
  /**
   * The message ID used to describe the attribute which configures
   * the subtree delete size limit.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_SUBTREE_DELETE_SIZE_LIMIT =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 35;
  /**
   * The message ID of an error indicating that the JE backend configuration
   * contains more than one configuration entry of a given object class where
   * only one entry of that object class is allowed.  This message takes two
@@ -493,57 +409,6 @@
       CATEGORY_MASK_JEB | SEVERITY_MASK_MILD_ERROR | 54;
  /**
   * The message ID used to describe the attribute which configures
   * the pathname of the directory for import temporary files.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_IMPORT_TEMP_DIRECTORY =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 55;
  /**
   * The message ID used to describe the attribute which configures
   * the amount of memory available for import buffering.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_IMPORT_BUFFER_SIZE =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 56;
  /**
   * The message ID used to describe the attribute which configures
   * the import queue size.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_IMPORT_QUEUE_SIZE =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 57;
  /**
   * The message ID used to describe the attribute which configures
   * the number of import worker threads.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_IMPORT_THREAD_COUNT =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 58;
  /**
   * The message ID used to describe the attribute which configures
   * the database cache eviction algorithm.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_EVICTOR_LRU_ONLY =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 59;
  /**
   * The message ID used to describe the attribute which configures
   * the number of nodes in one scan of the database cache evictor.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_EVICTOR_NODES_PER_SCAN =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 60;
  /**
   * The message ID used to log the size of the database cache after preloading.
   */
  public static final int MSGID_JEB_CACHE_SIZE_AFTER_PRELOAD =
@@ -552,15 +417,6 @@
  /**
   * The message ID used to describe the attribute which configures
   * the maximum time to spend preloading the database cache.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_PRELOAD_TIME_LIMIT =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 62;
  /**
   * The message ID for the message that will be used if an error occurs while
   * attempting to obtain the MAC provider for the backend backup.  This takes
   * two arguments, which are the name of the desired MAC algorithm and a string
@@ -746,24 +602,6 @@
  /**
   * The message ID used to describe the attribute which configures
   * whether entries should be compressed in the database.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_ENTRIES_COMPRESSED =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 80;
  /**
   * The message ID used to describe the attribute which configures
   * the maximum size of each individual JE log file, in bytes.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_DATABASE_LOG_FILE_MAX =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 81;
  /**
   * The message ID used to log an informational message in the backup process.
   * This message takes one argument, the name of a file that was not changed
   * since the previous backup.
@@ -1090,61 +928,6 @@
  /**
   * The message ID used to describe the attribute which configures
   * whether the logging file handler will be on or off.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_LOGGING_FILE_HANDLER_ON =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 113;
  /**
   * The message ID used to describe the attribute which configures
   * the trace log message level.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_LOGGING_LEVEL =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 114;
  /**
   * The message ID used to describe the attribute which configures
   * how many bytes are written to the log before the checkpointer runs.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_CHECKPOINT_BYTES_INTERVAL =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 115;
  /**
   * The message ID used to describe the attribute which configures
   * the amount of time between runs of the checkpointer.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_CHECKPOINT_WAKEUP_INTERVAL =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 116;
  /**
   * The message ID used to describe the attribute which configures
   * the number of times a database transaction will be retried after it is
   * aborted due to deadlock with another thread.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_DEADLOCK_RETRY_LIMIT =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 117;
  /**
   * The message ID used to describe the attribute which configures
   * the number of database lock tables.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_NUM_LOCK_TABLES =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 118;
  /**
   * The message ID used to log an informational message in the import process.
   * This message takes one argument, a string representation of the JE
   * environment configuration properties.
@@ -1154,15 +937,6 @@
  /**
   * The message ID used to describe the attribute which configures
   * the import pass size.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_IMPORT_PASS_SIZE =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 120;
  /**
   * The message ID used to indicate that an LDIF import pass has completed and
   * it is time to begin the intermediate index merge process.  This takes a
   * single argument, which is the pass number that has completed.
@@ -1204,15 +978,6 @@
  /**
   * The message ID used to describe the attribute which configures
   * the number of threads allocated by the cleaner for log file processing.
   */
  public static final int MSGID_CONFIG_DESCRIPTION_NUM_CLEANER_THREADS =
       CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 125;
  /**
   * The message ID of an error indicating the version of DatabaseEntry is
   * incompatible and can not be decoded.
   */
@@ -1308,7 +1073,7 @@
  /**
   * The message ID used to indicate that another rebuild process for an index
   * is alreadly in progress. This message takes the name of the conflicting
   * is already in progress. This message takes the name of the conflicting
   * index as the argument.
   */
  public static final int MSGID_JEB_REBUILD_INDEX_CONFLICT =
@@ -1339,68 +1104,32 @@
                    "The backend does not contain that part of the Directory " +
                    "Information Tree pertaining to the entry " +
                    "'%s'.");
    registerMessage(MSGID_JEB_OPEN_DATABASE_FAIL,
                    "The database could not be opened: %s.");
    registerMessage(MSGID_JEB_OPEN_ENV_FAIL,
                    "The database environment could not be opened: %s.");
/*
    registerMessage(MSGID_JEB_EMPTY_MESSAGE,
                    "");
*/
    registerMessage(MSGID_JEB_HIGHEST_ID_FAIL,
                    "The database highest entry identifier could not be " +
                    "determined.");
    registerMessage(MSGID_JEB_FUNCTION_NOT_SUPPORTED,
                    "The requested operation is not supported by this " +
                    "backend.");
    registerMessage(MSGID_JEB_CREATE_FAIL,
                    "The backend database directory could not be created: %s.");
    registerMessage(MSGID_JEB_REMOVE_FAIL,
                    "The backend database files could not be removed: %s.");
    registerMessage(MSGID_JEB_DIRECTORY_INVALID,
                    "The backend database directory '%s' is not a valid " +
                    "directory.");
    registerMessage(MSGID_JEB_MISSING_DN2ID_RECORD,
                    "The DN database does not contain a record for '%s'.");
    registerMessage(MSGID_JEB_MISSING_ID2ENTRY_RECORD,
                    "The entry database does not contain a record for ID %s.");
    registerMessage(MSGID_JEB_ENTRY_DATABASE_CORRUPT,
                    "The entry database does not contain a valid record " +
                    "for ID %s.");
/*
    registerMessage(MSGID_JEB_SUFFIXES_NOT_SPECIFIED,
                    "No suffixes specified for the backend.");
*/
    registerMessage(MSGID_JEB_DATABASE_EXCEPTION,
                    "Database exception: %s");
/*
    registerMessage(MSGID_JEB_JMX_CANNOT_REGISTER_MBEAN,
                    "The backend could not register a JMX MBean for " +
                    "the database in directory '%s':  " +
                    "%s ");
*/
    registerMessage(MSGID_CONFIG_DESCRIPTION_BACKEND_INDEX_ENTRY_LIMIT,
                    "A performance tuning parameter for attribute indexes. " +
                    "The default entry limit for attribute indexes, where " +
                    "a value of 0 means there is no limit. " +
                    "When the number of entries " +
                    "matching an index value reaches the limit, the " +
                    "value is no longer maintained in the index.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_INDEX_ATTRIBUTE,
                    "The attribute type name of the attribute index.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_INDEX_TYPE,
@@ -1414,65 +1143,13 @@
                    "When the number of entries " +
                    "matching an index value reaches the limit, the " +
                    "value is no longer maintained in the index.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_CACHE_PERCENT,
                    "The percentage of JVM memory to allocate to the database" +
                    " cache.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_CACHE_SIZE,
                    "The approximate amount of JVM memory to allocate to the " +
                    "database cache. The default value of zero indicates " +
                    "that the database cache is sized by the cachePercent " +
                    "configuration attribute. A non-zero value overrides the " +
                    "cachePercent configuration attribute.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_TXN_NO_SYNC,
                    "If true, do not write or synchronously flush the " +
                    "database log on transaction commit. This means that " +
                    "transactions exhibit the ACI (Atomicity, Consistency, " +
                    "and Isolation) properties, but not D (Durability); " +
                    "that is, database integrity is maintained, but if the " +
                    "JVM or operating system fails, it is possible some " +
                    "number of the most recently committed transactions " +
                    "may be undone during recovery. The number of " +
                    "transactions at risk is governed by how many " +
                    "updates fit into a log buffer, how often the " +
                    "operating system flushes dirty buffers to disk, and " +
                    "how often the database environment is checkpointed.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_TXN_WRITE_NO_SYNC,
                    "If true, write but do not synchronously flush the " +
                    "database log on transaction commit. This means that " +
                    "transactions exhibit the ACI (Atomicity, Consistency, " +
                    "and Isolation) properties, but not D (Durability); " +
                    "that is, database integrity is maintained, but if the " +
                    "JVM or operating system fails, it is possible some " +
                    "number of the most recently committed transactions may " +
                    "be undone during recovery. The number of transactions " +
                    "at risk is governed by how often the operating system " +
                    "flushes dirty buffers to disk, and how often the " +
                    "database environment is checkpointed. " +
                    "The motivation for this attribute is to provide a " +
                    "transaction that has more durability than asynchronous " +
                    "(nosync) transactions, but has higher performance than " +
                    "synchronous transactions. ");
    registerMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_RUN_CLEANER,
                    "If false, do not run the background cleaner thread " +
                    "responsible for freeing up disk space consumed by " +
                    "data records no longer in use.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_INDEX_SUBSTRING_LENGTH,
                    "The length of substrings in a substring index.");
    registerMessage(MSGID_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE,
                    "The attribute '%s' cannot have indexing of type '%s' " +
                    "because it does not have a corresponding matching rule.");
/*
    registerMessage(MSGID_JEB_CANNOT_ACQUIRE_LOCK,
                    "Unable to acquire a lock on the entry '%s'.");
*/
    registerMessage(MSGID_JEB_UNCHECKED_EXCEPTION,
                    "Unchecked exception during database transaction.");
    registerMessage(MSGID_JEB_CLEAN_DATABASE_START,
                    "Starting database cleaning on %d log file(s) in '%s'.");
    registerMessage(MSGID_JEB_CLEAN_DATABASE_MARKED,
@@ -1480,22 +1157,6 @@
    registerMessage(MSGID_JEB_CLEAN_DATABASE_FINISH,
                    "Finished database cleaning; " +
                    "now %d log file(s) remaining.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_CLEANER_MIN_UTILIZATION,
                    "A minimum log utilization property to determine how " +
                    "much database cleaning to perform.  Changes to this " +
                    "property do not take effect until the backend is " +
                    "restarted.  The log files " +
                    "contain both obsolete and utilized records. Obsolete " +
                    "records are records that are no longer in use, either " +
                    "because they have been modified or because they have " +
                    "been deleted.  Utilized records are those records that " +
                    "are currently in use. This property identifies the " +
                    "minimum percentage of log space that must be used by " +
                    "utilized records.  If this minimum percentage is not " +
                    "met, then log files are cleaned until the minimum " +
                    "percentage is met.");
    registerMessage(MSGID_JEB_SUBTREE_DELETE_SIZE_LIMIT_EXCEEDED,
                    "Exceeded the administrative limit on the number of " +
                    "entries that may be deleted in a subtree delete " +
@@ -1504,14 +1165,6 @@
                    "in the subtree have been deleted.");
    registerMessage(MSGID_JEB_DELETED_ENTRY_COUNT,
                    "The number of entries deleted was %d.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_SUBTREE_DELETE_SIZE_LIMIT,
                    "The maximum number of entries that can be deleted " +
                    "by a delete entry operation with the subtree delete " +
                    "control specified. To delete subtrees containing more " +
                    "entries than this value, the operation must be repeated " +
                    "as many times as necessary to complete the subtree " +
                    "delete.");
    registerMessage(MSGID_JEB_DUPLICATE_CONFIG_ENTRY,
                    "The configuration entry '%s' will be ignored. " +
                    "Only one configuration entry with object class '%s' is " +
@@ -1527,27 +1180,17 @@
                    "because it specifies the attribute type '%s', " +
                    "which has already been defined in another " +
                    "index configuration entry.");
    registerMessage(MSGID_JEB_IO_ERROR,
                    "I/O error during backend operation: %s");
/*
    registerMessage(MSGID_JEB_INDEX_THREAD_EXCEPTION,
                    "An index worker thread raised an exception: %s");
*/
    registerMessage(MSGID_JEB_BACKEND_STARTED,
                    "A database backend containing %d entries has started.");
    registerMessage(MSGID_JEB_IMPORT_PARENT_NOT_FOUND,
                    "The parent entry '%s' does not exist.");
    registerMessage(MSGID_JEB_IMPORT_ENTRY_EXISTS,
                    "The entry exists and the import options do not " +
                    "allow it to be replaced.");
    registerMessage(MSGID_JEB_ATTRIBUTE_INDEX_NOT_CONFIGURED,
                    "There is no index configured for attribute type '%s'.");
    registerMessage(MSGID_JEB_SEARCH_NO_SUCH_OBJECT,
                    "The search base entry '%s' does not exist.");
    registerMessage(MSGID_JEB_ADD_NO_SUCH_OBJECT,
@@ -1574,64 +1217,8 @@
    registerMessage(MSGID_JEB_NEW_SUPERIOR_NO_SUCH_OBJECT,
                    "The entry cannot be moved because the new parent " +
                    "entry '%s' does not exist.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_IMPORT_TEMP_DIRECTORY,
                    "The pathname of a directory to hold temporary working " +
                    "files generated during an import process.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_IMPORT_BUFFER_SIZE,
                    "The amount of memory that is available to an import " +
                    "process for its buffers.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_IMPORT_QUEUE_SIZE,
                    "The maximum number of entries that can be on the import " +
                    "queue waiting to be processed.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_IMPORT_THREAD_COUNT,
                    "The number of worker threads that will be created to " +
                    "import entries from LDIF.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_IMPORT_PASS_SIZE,
                    "The maximum number of entries that can be processed in " +
                    "a single pass before index merging occurs.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_EVICTOR_LRU_ONLY,
                    "Changes to this property do not take effect until the " +
                    "backend is restarted.  " +
                    "The default database cache eviction algorithm is LRU " +
                    "(least recently used) and the default setting for the " +
                    "lruOnly parameter is true. With the default algorithm, " +
                    "only the LRU of a Btree node is taken into account " +
                    "when choosing the next Btree node to be evicted.  " +
                    "When lruOnly is set to false, the eviction algorithm is " +
                    "instead primarily based on the level of the node in " +
                    "the Btree. The lowest level nodes (the leaf nodes) are " +
                    "always evicted first, even if higher level nodes are " +
                    "less recently used. In addition, dirty nodes are " +
                    "evicted after non-dirty nodes.  " +
                    "Setting lruOnly to false benefits random access " +
                    "applications because it keeps higher level Btree " +
                    "nodes in the tree for as long as possible. For a " +
                    "random key, this increases the likelihood that the " +
                    "relevant Btree internal nodes will be in the cache.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_EVICTOR_NODES_PER_SCAN,
                    "Changes to this property do not take effect until the " +
                    "backend is restarted.  " +
                    "It is recommended that you also change nodesPerScan " +
                    "when you set lruOnly to false. This setting controls " +
                    "the number of Btree nodes that are considered, or " +
                    "sampled, each time a node is evicted. A setting of 100 " +
                    "often produces good results, but this may vary from " +
                    "application to application. The larger the " +
                    "nodesPerScan, the more accurate the algorithm. However, " +
                    "setting it too high is detrimental; the need to " +
                    "consider larger numbers of nodes for each eviction may " +
                    "delay the completion of a given database operation, " +
                    "which will impact the response time of the application " +
                    "thread.");
    registerMessage(MSGID_JEB_CACHE_SIZE_AFTER_PRELOAD,
                    "The database cache is %d MB after pre-loading.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_PRELOAD_TIME_LIMIT,
                    "The maximum time to spend pre-loading the database " +
                    "cache when the backend starts. The time units may be ms " +
                    "(milliseconds), s (seconds) or m (minutes). A value of " +
                    "zero means there will be no pre-load.");
    registerMessage(MSGID_JEB_BACKUP_CANNOT_GET_MAC,
                    "An error occurred while attempting to obtain the %s MAC " +
                    "provider to create the signed hash for the backup:  %s.");
@@ -1684,15 +1271,6 @@
    registerMessage(MSGID_JEB_BACKUP_MISSING_BACKUPID,
                    "The information for backup %s could not be found in " +
                    "the backup directory %s.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_ENTRIES_COMPRESSED,
                    "Set to true to indicate that the backend should " +
                    "attempt to compress entries when writing " +
                    "to the database.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_DATABASE_LOG_FILE_MAX,
                    "The maximum size of each individual database log file, " +
                    "in bytes.  Changes to this property do not take effect " +
                    "until the backend is restarted.");
    registerMessage(MSGID_JEB_BACKUP_FILE_UNCHANGED,
                    "Not changed: %s");
    registerMessage(MSGID_JEB_BACKUP_CLEANER_ACTIVITY,
@@ -1780,40 +1358,8 @@
    registerMessage(MSGID_JEB_REFERRAL_RESULT_MESSAGE,
                    "A referral entry %s indicates that the operation must " +
                    "be processed at a different server.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_LOGGING_FILE_HANDLER_ON,
                    "Determines whether database trace logging is written to " +
                    "a je.info file in the backend database directory.  " +
                    "Changes to this property do not take effect until the " +
                    "backend is restarted.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_LOGGING_LEVEL,
                    "The database trace logging level chosen from: SEVERE, " +
                    "WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL or " +
                    "OFF.  Changes to this property do not take effect until " +
                    "the backend is restarted.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_CHECKPOINT_BYTES_INTERVAL,
                    "The checkpointer runs every time this number of bytes" +
                    "have been written to the database log.  If this property" +
                    "is set to a non-zero value, the checkpointer wakeup " +
                    "interval is not used.  To use time based checkpointing, " +
                    "set this property to zero.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_CHECKPOINT_WAKEUP_INTERVAL,
                    "The checkpointer wakeup interval.  If the checkpointer " +
                    "bytes interval is zero, the checkpointer runs at time " +
                    "intervals determined by this property.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_DEADLOCK_RETRY_LIMIT,
                    "The number of times a database transaction will be " +
                    "retried after it has been aborted due to deadlock with " +
                    "another thread.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_NUM_LOCK_TABLES,
                    "The number of database lock tables.  It should be set " +
                    "to a prime number, and in general not higher than the " +
                    "number of server worker threads.");
    registerMessage(MSGID_JEB_IMPORT_ENVIRONMENT_CONFIG,
                    "Database environment properties: %s.");
    registerMessage(MSGID_CONFIG_DESCRIPTION_NUM_CLEANER_THREADS,
                    "The number of threads allocated by the cleaner for log " +
                    "file processing. If the cleaner backlog becomes large, " +
                    "increase this number.");
    registerMessage(MSGID_JEB_INCOMPATIBLE_ENTRY_VERSION,
                    "Entry record with ID %s is not compatible with this " +
                    "version of the backend database. " +
@@ -1844,7 +1390,7 @@
                    "An error occured while inserting entry into the %s " +
                    "database/index: %s");
    registerMessage(MSGID_JEB_REBUILD_INDEX_CONFLICT,
                    "Another rebuild of index %s is alreadly in progress.");
                    "Another rebuild of index %s is already in progress.");
    registerMessage(MSGID_JEB_REBUILD_BACKEND_ONLINE,
                    "Rebuilding system index(es) must be done with the " +
                    "backend containing the base DN disabled.");
opends/src/server/org/opends/server/messages/UtilityMessages.java
@@ -1918,7 +1918,7 @@
    registerMessage(MSGID_SUBCMDPARSER_DUPLICATE_GLOBAL_ARG_NAME,
                    "There is already another global argument named \"%s\".");
    registerMessage(MSGID_SUBCMDPARSER_GLOBAL_ARG_NAME_SUBCMD_CONFLICT,
                    "The argument name %s conflicts the the name of another " +
                    "The argument name %s conflicts with the name of another " +
                    "argument associated with the %s subcommand.");
    registerMessage(MSGID_SUBCMDPARSER_DUPLICATE_GLOBAL_ARG_SHORT_ID,
                    "Short ID -%s for global argument %s conflicts with the " +
opends/tests/unit-tests-testng/resource/config-changes.ldif
@@ -378,28 +378,6 @@
ds-cfg-index-type: ordering
ds-cfg-index-type: approximate
dn: cn=JE Database,ds-cfg-backend-id=rebuildRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-database
cn: JE Database
ds-cfg-database-cache-percent: 10
ds-cfg-database-cache-size: 0 megabytes
ds-cfg-database-txn-no-sync: false
ds-cfg-database-txn-write-no-sync: true
ds-cfg-database-run-cleaner: true
ds-cfg-database-cleaner-num-threads: 1
ds-cfg-database-cleaner-min-utilization: 75
ds-cfg-database-evictor-lru-only: true
ds-cfg-database-evictor-nodes-per-scan: 10
ds-cfg-database-log-file-max: 50 megabytes
ds-cfg-database-logging-file-handler-on: true
ds-cfg-database-logging-level: CONFIG
ds-cfg-database-checkpointer-bytes-interval: 20 megabytes
ds-cfg-database-checkpointer-wakeup-interval: 30 seconds
ds-cfg-database-lock-num-lock-tables: 19
dn: ds-cfg-backend-id=verifyRoot,cn=Backends,cn=config
changetype: add
objectClass: top
@@ -482,7 +460,6 @@
ds-cfg-index-type: equality
ds-cfg-index-type: substring
dn: ds-cfg-index-attribute=uid,cn=Index,ds-cfg-backend-id=verifyRoot,cn=Backends,cn=config
changetype: add
objectClass: top
@@ -504,26 +481,131 @@
ds-cfg-index-attribute: entryuuid
ds-cfg-index-type: equality
dn: cn=JE Database,ds-cfg-backend-id=verifyRoot,cn=Backends,cn=config
dn: ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-database
cn: JE Database
ds-cfg-database-cache-percent: 10
ds-cfg-database-cache-size: 0 megabytes
ds-cfg-database-txn-no-sync: false
ds-cfg-database-txn-write-no-sync: true
ds-cfg-database-run-cleaner: true
ds-cfg-database-cleaner-num-threads: 1
ds-cfg-database-cleaner-min-utilization: 75
ds-cfg-database-evictor-lru-only: true
ds-cfg-database-evictor-nodes-per-scan: 10
ds-cfg-database-log-file-max: 50 megabytes
ds-cfg-database-logging-file-handler-on: true
ds-cfg-database-logging-level: CONFIG
ds-cfg-database-checkpointer-bytes-interval: 20 megabytes
ds-cfg-database-checkpointer-wakeup-interval: 30 seconds
ds-cfg-database-lock-num-lock-tables: 19
objectClass: ds-cfg-backend
objectClass: ds-cfg-je-backend
ds-cfg-backend-enabled: true
ds-cfg-backend-class: org.opends.server.backends.jeb.BackendImpl
ds-cfg-backend-id: indexTestRoot
ds-cfg-backend-writability-mode: enabled
ds-cfg-backend-base-dn: dc=test,dc=com
ds-cfg-backend-base-dn: dc=test1,dc=com
ds-cfg-backend-directory: db_index_test
ds-cfg-backend-mode: 700
ds-cfg-backend-index-entry-limit: 13
ds-cfg-backend-subtree-delete-size-limit: 100000
ds-cfg-backend-preload-time-limit: 0 seconds
ds-cfg-backend-import-temp-directory: importTmp
ds-cfg-backend-import-buffer-size: 256 megabytes
ds-cfg-backend-import-queue-size: 100
ds-cfg-backend-import-pass-size: 0
ds-cfg-backend-import-thread-count: 8
ds-cfg-backend-entries-compressed: false
ds-cfg-backend-deadlock-retry-limit: 10
dn: cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-branch
cn: Index
dn: ds-cfg-index-attribute=cn,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-index
ds-cfg-index-attribute: cn
ds-cfg-index-type: presence
ds-cfg-index-type: equality
ds-cfg-index-type: substring
ds-cfg-index-type: ordering
ds-cfg-index-type: approximate
dn: ds-cfg-index-attribute=employeeNumber,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-index
ds-cfg-index-attribute: employeeNumber
ds-cfg-index-type: presence
ds-cfg-index-type: equality
ds-cfg-index-type: substring
ds-cfg-index-type: ordering
dn: ds-cfg-index-attribute=title,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-index
ds-cfg-index-attribute: title
ds-cfg-index-type: presence
ds-cfg-index-type: equality
ds-cfg-index-type: substring
ds-cfg-index-type: ordering
dn: ds-cfg-index-attribute=givenName,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-index
ds-cfg-index-attribute: givenName
ds-cfg-index-type: presence
ds-cfg-index-type: equality
ds-cfg-index-type: substring
dn: ds-cfg-index-attribute=mail,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-index
ds-cfg-index-attribute: mail
ds-cfg-index-type: presence
ds-cfg-index-type: equality
ds-cfg-index-type: substring
ds-cfg-index-type: ordering
dn: ds-cfg-index-attribute=member,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-index
ds-cfg-index-attribute: member
ds-cfg-index-type: equality
dn: ds-cfg-index-attribute=sn,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-index
ds-cfg-index-attribute: sn
ds-cfg-index-type: presence
ds-cfg-index-type: equality
ds-cfg-index-type: substring
dn: ds-cfg-index-attribute=telephoneNumber,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-index
ds-cfg-index-attribute: telephoneNumber
ds-cfg-index-type: presence
ds-cfg-index-type: equality
ds-cfg-index-type: substring
dn: ds-cfg-index-attribute=uid,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-index
ds-cfg-index-attribute: uid
ds-cfg-index-type: equality
dn: ds-cfg-index-attribute=ds-sync-hist,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-index
ds-cfg-index-attribute: ds-sync-hist
ds-cfg-index-type: ordering
dn: ds-cfg-index-attribute=entryuuid,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
changetype: add
objectClass: top
objectClass: ds-cfg-je-index
ds-cfg-index-attribute: entryuuid
ds-cfg-index-type: equality
dn: cn=Virtual Static member,cn=Virtual Attributes,cn=config
changetype: modify
opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
@@ -190,7 +190,8 @@
    //db_rebuild is the third jeb backend used by the jeb rebuild test cases
    String[] subDirectories = { "bak", "bin", "changelogDb", "classes",
                                "config", "db", "db_verify", "ldif", "lib",
                                "locks", "logs", "db_rebuild" };
                                "locks", "logs", "db_rebuild",
                                "db_index_test" };
    for (String s : subDirectories)
    {
      new File(testRoot, s).mkdir();
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaBackendTestCase.java
@@ -5377,7 +5377,7 @@
         throws Exception
  {
    DN configEntryDN =
            DN.decode("ds-cfg-backend-id=schema,cn=Backends,cn=config");
            DN.decode("cn=schema,cn=Backends,cn=config");
    DN[] baseDNs = { DN.decode("cn=schema") };
    ConfigEntry configEntry =
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
@@ -26,31 +26,26 @@
 */
package org.opends.server.backends.jeb;
import java.io.File;
import java.util.*;
import org.opends.server.TestCaseUtils;
import static
    org.opends.server.messages.ConfigMessages.MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
import static org.opends.server.messages.MessageHandler.getMessage;
import org.opends.server.admin.std.server.JEBackendCfg;
import org.opends.server.admin.std.meta.JEBackendCfgDefn;
import org.opends.server.admin.server.AdminTestCaseUtils;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.protocols.ldap.LDAPControl;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.DNConfigAttribute;
import static org.opends.server.config.ConfigConstants.ATTR_BACKEND_BASE_DN;
import org.opends.server.types.*;
import org.opends.server.util.Base64;
import static
    org.opends.server.util.ServerConstants.OID_SUBTREE_DELETE_CONTROL;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.AfterTest;
import org.testng.annotations.AfterClass;
import static org.testng.Assert.*;
@@ -61,10 +56,8 @@
 * BackendImpl Tester.
 */
public class TestBackendImpl extends JebTestCase {
  private File tempDir;
  private String homeDirName;
  private DN[] baseDNs;
  private BackendImpl backend;
  private List<Entry> topEntries;
@@ -84,81 +77,9 @@
    // sure the server is started.
    TestCaseUtils.startServer();
    tempDir = TestCaseUtils.createTemporaryDirectory("jebimporttest");
    homeDirName = tempDir.getAbsolutePath();
    homeDirName = "db_index_test";
    EnvManager.createHomeDir(homeDirName);
    Entry configEntry = TestCaseUtils.makeEntry(
        "dn: ds-cfg-backend-id=userRoot,cn=Backends,cn=config",
        "objectClass: top",
        "objectClass: ds-cfg-backend",
        "objectClass: ds-cfg-je-backend",
        "ds-cfg-backend-index-entry-limit: 13",
        "ds-cfg-backend-enabled: true",
        "ds-cfg-backend-class: org.opends.server.backends.jeb.BackendImpl",
        "ds-cfg-backend-id: userRoot",
        "ds-cfg-backend-directory:: " +
            Base64.encode(homeDirName.getBytes()));
    ConfigEntry backendConfigEntry = new ConfigEntry(configEntry, null);
    Entry indexEntry = TestCaseUtils.makeEntry(
        "dn: cn=Index,ds-cfg-backend-id=userRoot,cn=Backends,cn=config\n" +
            "objectClass: top\n" +
            "objectClass: ds-cfg-branch\n" +
            "cn: Index\n");
    ConfigEntry indexConfigEntry = new ConfigEntry(indexEntry,
        backendConfigEntry);
    backendConfigEntry.addChild(indexConfigEntry);
    List<Entry> indexEntries = TestCaseUtils.makeEntries(
        "dn: ds-cfg-index-attribute=cn,cn=Index,ds-cfg-backend-id=userRoot,cn=Backends,cn=config\n" +
            "objectClass: top\n" +
            "objectClass: ds-cfg-je-index\n" +
            "ds-cfg-index-attribute: cn\n" +
            "ds-cfg-index-type: presence\n" +
            "ds-cfg-index-type: equality\n" +
            "ds-cfg-index-type: substring\n" +
            "ds-cfg-index-type: ordering\n" +
            "ds-cfg-index-type: approximate\n" +
            "\n" +
            "dn: ds-cfg-index-attribute=employeeNumber,cn=Index,ds-cfg-backend-id=userRoot,cn=Backends,cn=config\n" +
            "objectClass: top\n" +
            "objectClass: ds-cfg-je-index\n" +
            "ds-cfg-index-attribute: employeeNumber\n" +
            "ds-cfg-index-type: presence\n" +
            "ds-cfg-index-type: equality\n" +
            "ds-cfg-index-type: substring\n" +
            "ds-cfg-index-type: ordering\n" +
            "\n" +
            "dn: ds-cfg-index-attribute=title,cn=Index,ds-cfg-backend-id=userRoot,cn=Backends,cn=config\n" +
            "objectClass: top\n" +
            "objectClass: ds-cfg-je-index\n" +
            "ds-cfg-index-attribute: title\n" +
            "ds-cfg-index-type: presence\n" +
            "ds-cfg-index-type: equality\n" +
            "ds-cfg-index-type: substring\n" +
            "ds-cfg-index-type: ordering\n");
    ConfigEntry attribIndexConfigEntry;
    for (Entry attribIndexEntry : indexEntries) {
      attribIndexConfigEntry = new ConfigEntry(attribIndexEntry,
          indexConfigEntry);
      indexConfigEntry.addChild(attribIndexConfigEntry);
    }
    baseDNs = new DN[]
        {
            DN.decode("dc=test,dc=com"),
            DN.decode("dc=test1,dc=com")
        };
    backend = new BackendImpl();
    backend.initializeBackend(backendConfigEntry, baseDNs);
    backend = (BackendImpl)DirectoryServer.getBackend("indexTestRoot");
    topEntries = TestCaseUtils.makeEntries(
        "dn: dc=test,dc=com",
@@ -594,8 +515,6 @@
  @AfterClass
  public void cleanUp() throws Exception {
    backend.finalizeBackend();
    TestCaseUtils.deleteDirectory(tempDir);
  }
  @Test(expectedExceptions = DirectoryException.class)
@@ -620,7 +539,6 @@
  @Test(dependsOnMethods = "testAdd")
  public void testSearchScope() throws Exception {
    ArrayList<Control> noControls = new ArrayList<Control>(0);
    InternalClientConnection conn =
        InternalClientConnection.getRootConnection();
@@ -667,7 +585,6 @@
  @Test(dependsOnMethods = "testAdd")
  public void testSearchIndex() throws Exception {
    ArrayList<Control> noControls = new ArrayList<Control>(0);
    InternalClientConnection conn =
        InternalClientConnection.getRootConnection();
    LinkedHashSet<String> attribs = new LinkedHashSet<String>();
@@ -923,7 +840,6 @@
    AttributeIndex index;
    HashSet<ASN1OctetString> addKeys;
    DatabaseEntry key;
    PresenceIndexer presenceIndexer;
    EqualityIndexer equalityIndexer;
    SubstringIndexer substringIndexer;
    OrderingIndexer orderingIndexer;
@@ -1235,21 +1151,24 @@
      "testModifyDNNewSuperior"})
  public void testApplyConfig() throws Exception {
    Entry configEntry = TestCaseUtils.makeEntry(
        "dn: ds-cfg-backend-id=userRoot,cn=Backends,cn=config",
        "dn: ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
        "objectClass: top",
        "objectClass: ds-cfg-backend",
        "objectClass: ds-cfg-je-backend",
        "ds-cfg-backend-base-dn: dc=test,dc=com",
        "ds-cfg-backend-base-dn: dc=newsuffix,dc=com",
        "ds-cfg-backend-enabled: true",
        "ds-cfg-backend-writability-mode: enabled",
        "ds-cfg-backend-class: org.opends.server.backends.jeb.BackendImpl",
        "ds-cfg-backend-id: userRoot",
        "ds-cfg-backend-id: indexTestRoot",
        "ds-cfg-backend-directory:: " +
            Base64.encode(homeDirName.getBytes()));
            Base64.encode(homeDirName.getBytes()),
        "ds-cfg-backend-import-temp-directory: importTmp");
    ConfigEntry backendConfigEntry = new ConfigEntry(configEntry, null);
    JEBackendCfg cfg = AdminTestCaseUtils.getConfiguration(
         JEBackendCfgDefn.getInstance(), configEntry);
    backend.applyNewConfiguration(backendConfigEntry, true);
    backend.applyConfigurationChange(cfg);
    RootContainer rootContainer = backend.getRootContainer();
@@ -1267,16 +1186,12 @@
      assertNotNull(backend.getEntry(entry.getDN()));
    }
    ArrayList<Control> noControls = new ArrayList<Control>(0);
    InternalClientConnection conn =
        InternalClientConnection.getRootConnection();
    LinkedHashSet<String> attribs = new LinkedHashSet<String>();
    attribs.add(ATTR_DEBUG_SEARCH_INDEX);
    String debugString;
    int finalStartPos;
    int finalEndPos;
    int finalCount;
    InternalSearchOperation search =
        conn.processSearch(DN.decode("dc=test,dc=com"),
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestEntryContainer.java
@@ -28,26 +28,21 @@
import static org.testng.AssertJUnit.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import org.opends.server.TestCaseUtils;
import org.opends.server.core.DirectoryServer;
import org.opends.server.admin.std.server.JEBackendCfg;
import org.opends.server.admin.std.meta.JEBackendCfgDefn;
import org.opends.server.admin.server.AdminTestCaseUtils;
import org.opends.server.types.Entry;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.FilePermission;
import org.opends.server.types.DN;
import org.opends.server.util.LDIFReader;
import org.opends.server.util.Base64;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.AfterClass;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
/**
 * EntryContainer tester.
 */
@@ -121,7 +116,7 @@
  private File tempDir;
  private String homeDirName;
  private ArrayList<Entry> entryList;
  private List<Entry> entryList;
  private long calculatedHighestID = 0;
@@ -140,28 +135,11 @@
    tempDir = TestCaseUtils.createTemporaryDirectory("jebtest");
    homeDirName = tempDir.getAbsolutePath();
    // Convert the test LDIF string to a byte array
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(
        byteArrayOutputStream);
    outputStreamWriter.write(ldifString);
    outputStreamWriter.flush();
    byte[] originalLDIFBytes = byteArrayOutputStream.toByteArray();
    LDIFReader reader = new LDIFReader(new LDIFImportConfig(
        new ByteArrayInputStream(originalLDIFBytes)));
    // Create a set of entries
    entryList = new ArrayList<Entry>();
    long entryID = 0;
    Entry entry;
    while ((entry = reader.readEntry(false)) != null) {
      entryID++;
      entryList.add(entry);
    }
    entryList = TestCaseUtils.entriesFromLdifString(ldifString);
    // Remember the highest entryID
    calculatedHighestID = entryID;
    calculatedHighestID = entryList.size();
  }
  /**
@@ -184,13 +162,32 @@
  @Test()
  public void test1() throws Exception {
    EnvManager.createHomeDir(homeDirName);
    RootContainer rootContainer = new RootContainer(new Config(), null);
    Entry configEntry = TestCaseUtils.makeEntry(
         "dn: ds-cfg-backend-id=userRoot,cn=Backends,cn=config",
              "objectClass: top",
              "objectClass: ds-cfg-backend",
              "objectClass: ds-cfg-je-backend",
              "ds-cfg-backend-enabled: true",
              "ds-cfg-backend-class: " +
                   "org.opends.server.backends.jeb.BackendImpl",
              "ds-cfg-backend-id: userRoot",
              "ds-cfg-backend-writability-mode: enabled",
              "ds-cfg-backend-base-dn: dc=com",
              "ds-cfg-backend-directory:: " +
                   Base64.encode(homeDirName.getBytes()),
              "ds-cfg-backend-import-temp-directory: importTmp");
    JEBackendCfg cfg = AdminTestCaseUtils.getConfiguration(
         JEBackendCfgDefn.getInstance(), configEntry);
    Config backendConfig = new Config();
    backendConfig.initializeConfig(cfg,
                                   cfg.getBackendBaseDN().toArray(new DN[0]));
    RootContainer rootContainer = new RootContainer(backendConfig, null);
    rootContainer.open(new File(homeDirName),
                       new FilePermission(true, true, true),
                       false, true, true, false, true, true);
    EntryContainer entryContainer =
        rootContainer.openEntryContainer(DirectoryServer.getSchemaDN());
        rootContainer.openEntryContainer(DN.decode("dc=com"));
    EntryID actualHighestID = entryContainer.getHighestEntryID();
    assertTrue(actualHighestID.equals(new EntryID(0)));
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestImportJob.java
@@ -238,6 +238,9 @@
      "objectClass: top",
      "objectClass: ds-cfg-backend",
      "objectClass: ds-cfg-je-backend",
      "ds-cfg-backend-base-dn: dc=example,dc=com",
      "ds-cfg-backend-base-dn: dc=example1,dc=com",
      "ds-cfg-backend-writability-mode: enabled",
      "ds-cfg-backend-enabled: true",
      "ds-cfg-backend-class: org.opends.server.backends.jeb.BackendImpl",
      "ds-cfg-backend-id: userRoot",