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

neil_a_wilson
10.08.2007 94bbd5bb7c20a490558d8ec97d1be7e3dc492a42
Update the server to provide a basic framework for controlling when plugins
will be invoked. There are two basic changes:

- Add a new ds-cfg-invoke-for-internal-operations configuration attribute for
all plugins, which indicates whether the plugin should be invoked for
internal operations. If this is false, then the plugin will only be invoked
for externally-requested operations.

- Add four new plugin types: postSynchronizationAdd,
postSynchronizationDelete, postSynchronizationModify, and
postSynchronizationModifyDN. These allow a plugin to perform a limited set
of processing for changes that are successfully applied through
synchronization.


The unique attribute plugin has also been updated to support the
post-synchronization plugin types so that if a conflict is introduced
concurrently on two different servers within the propagation delay, an
administrative alert will be generated to indicate that manual intervention is
required to address the problem.

Finally, ensure that audit logging is enabled during the unit tests, and
update the audit logger to include the connection ID and operation ID for the
operation being logged.


OpenDS Issue Number: 2057
5 files added
20 files modified
3277 ■■■■ changed files
opends/resource/config/config.ldif 11 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif 27 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/PluginConfiguration.xml 54 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/PluginRootConfiguration.xml 117 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/UniqueAttributePluginConfiguration.xml 3 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/plugin.properties 16 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/plugin/DirectoryServerPlugin.java 139 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/plugin/PluginType.java 90 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/PluginConfigManager.java 606 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java 16 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java 1137 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/operation/PostSynchronizationAddOperation.java 136 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/operation/PostSynchronizationDeleteOperation.java 78 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/operation/PostSynchronizationModifyDNOperation.java 189 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/operation/PostSynchronizationModifyOperation.java 148 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/operation/PostSynchronizationOperation.java 144 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/ServerConstants.java 44 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java 4 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java 4 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java 4 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java 4 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java 44 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/resource/config-changes.ldif 20 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/api/plugin/DirectoryServerPluginTestCase.java 176 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/InvocationCounterPlugin.java 66 ●●●●● patch | view | raw | blame | history
opends/resource/config/config.ldif
@@ -1451,6 +1451,7 @@
ds-cfg-7-bit-clean-attribute-type: uid
ds-cfg-7-bit-clean-attribute-type: mail
ds-cfg-7-bit-clean-attribute-type: userPassword
ds-cfg-invoke-for-internal-operations: true
dn: cn=Entry UUID,cn=Plugins,cn=config
objectClass: top
@@ -1461,6 +1462,7 @@
ds-cfg-plugin-enabled: true
ds-cfg-plugin-type: ldifImport
ds-cfg-plugin-type: preOperationAdd
ds-cfg-invoke-for-internal-operations: true
dn: cn=LastMod,cn=Plugins,cn=config
objectClass: top
@@ -1472,6 +1474,7 @@
ds-cfg-plugin-type: preOperationAdd
ds-cfg-plugin-type: preOperationModify
ds-cfg-plugin-type: preOperationModifyDN
ds-cfg-invoke-for-internal-operations: true
dn: cn=LDAP Attribute Description List,cn=Plugins,cn=config
objectClass: top
@@ -1481,6 +1484,7 @@
ds-cfg-plugin-class: org.opends.server.plugins.LDAPADListPlugin
ds-cfg-plugin-enabled: true
ds-cfg-plugin-type: preParseSearch
ds-cfg-invoke-for-internal-operations: true
dn: cn=Password Policy Import,cn=Plugins,cn=config
objectClass: top
@@ -1492,6 +1496,7 @@
ds-cfg-plugin-type: ldifImport
ds-cfg-default-user-password-storage-scheme-dn: cn=Salted SHA-1,cn=Password Storage Schemes,cn=config
ds-cfg-default-auth-password-storage-scheme-dn: cn=Salted SHA-1,cn=Password Storage Schemes,cn=config
ds-cfg-invoke-for-internal-operations: false
dn: cn=Profiler,cn=Plugins,cn=config
objectClass: top
@@ -1504,6 +1509,7 @@
ds-cfg-enable-profiling-on-startup: false
ds-cfg-profile-directory: logs
ds-cfg-profile-sample-interval: 10 milliseconds
ds-cfg-invoke-for-internal-operations: false
dn: cn=Referential Integrity,cn=Plugins,cn=config
objectClass: top
@@ -1517,6 +1523,7 @@
ds-cfg-plugin-type: subordinateModifyDN
ds-cfg-referential-integrity-attribute-type: member
ds-cfg-referential-integrity-attribute-type: uniqueMember
ds-cfg-invoke-for-internal-operations: true
dn: cn=UID Unique Attribute,cn=Plugins,cn=config
objectClass: top
@@ -1528,7 +1535,11 @@
ds-cfg-plugin-type: preOperationAdd
ds-cfg-plugin-type: preOperationModify
ds-cfg-plugin-type: preOperationModifyDN
ds-cfg-plugin-type: postSynchronizationAdd
ds-cfg-plugin-type: postSynchronizationModify
ds-cfg-plugin-type: postSynchronizationModifyDN
ds-cfg-unique-attribute-type: uid
ds-cfg-invoke-for-internal-operations: true
dn: cn=Root DNs,cn=config
objectClass: top
opends/resource/schema/02-config.ldif
@@ -1658,6 +1658,26 @@
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.490 NAME 'ds-cfg-poll-interval'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.491
  NAME 'ds-cfg-plugin-order-synchronization-add'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.492
  NAME 'ds-cfg-plugin-order-synchronization-delete'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.493
  NAME 'ds-cfg-plugin-order-synchronization-modify'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.494
  NAME 'ds-cfg-plugin-order-synchronization-modify-dn'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.495
  NAME 'ds-cfg-invoke-for-internal-operations'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
  MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1847,7 +1867,8 @@
  ds-cfg-password-validator-enabled ) X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.37 NAME 'ds-cfg-plugin' SUP top
  STRUCTURAL MUST ( cn $ ds-cfg-plugin-class $ ds-cfg-plugin-enabled $
  ds-cfg-plugin-type ) X-ORIGIN 'OpenDS Directory Server' )
  ds-cfg-plugin-type ) MAY ds-cfg-invoke-for-internal-operations
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.38 NAME 'ds-cfg-profiler-plugin'
  SUP ds-cfg-plugin STRUCTURAL MAY ( ds-cfg-enable-profiling-on-startup $
  ds-cfg-profile-directory $ ds-cfg-profile-sample-interval $
@@ -2254,6 +2275,10 @@
  ds-cfg-plugin-order-post-response-modify $
  ds-cfg-plugin-order-post-response-modify-dn $
  ds-cfg-plugin-order-post-response-search $
  ds-cfg-plugin-order-synchronization-add $
  ds-cfg-plugin-order-synchronization-delete $
  ds-cfg-plugin-order-synchronization-modify $
  ds-cfg-plugin-order-synchronization-modify-dn $
  ds-cfg-plugin-order-search-result-entry $
  ds-cfg-plugin-order-search-result-reference $
  ds-cfg-plugin-order-subordinate-modify-dn $
opends/src/admin/defn/org/opends/server/admin/std/PluginConfiguration.xml
@@ -31,6 +31,7 @@
  package="org.opends.server.admin.std" abstract="false"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    <adm:user-friendly-plural-name />
    provide a mechanism for executing custom code at specified points in
@@ -38,7 +39,9 @@
    establishment and termination, server startup and shutdown, and LDIF import
    and export.
  </adm:synopsis>
  <adm:tag name="core"/>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.37</ldap:oid>
@@ -46,6 +49,7 @@
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="enabled" mandatory="true">
    <adm:synopsis>
      Indicate whether the
@@ -62,6 +66,7 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="plugin-class" mandatory="true">
    <adm:synopsis>
      The fully-qualified name of the Java class that provides the
@@ -82,6 +87,7 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="plugin-type" mandatory="true" multi-valued="true">
    <adm:synopsis>
      The plugin types, which define the conditions under which this plugin
@@ -308,6 +314,30 @@
            Invoked after sending the search result done message to the client.
          </adm:synopsis>
        </adm:value>
        <adm:value name="postsynchronizationadd">
          <adm:synopsis>
            Invoked after completing post-synchronization processing for an add
            operation.
          </adm:synopsis>
        </adm:value>
        <adm:value name="postsynchronizationdelete">
          <adm:synopsis>
            Invoked after completing post-synchronization processing for a
            delete operation.
          </adm:synopsis>
        </adm:value>
        <adm:value name="postsynchronizationmodify">
          <adm:synopsis>
            Invoked after completing post-synchronization processing for a
            modify operation.
          </adm:synopsis>
        </adm:value>
        <adm:value name="postsynchronizationmodifydn">
          <adm:synopsis>
            Invoked after completing post-synchronization processing for a
            modify DN operation.
          </adm:synopsis>
        </adm:value>
        <adm:value name="searchresultentry">
          <adm:synopsis>
            Invoked before sending a search result entry to the client.
@@ -339,5 +369,29 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="invoke-for-internal-operations" mandatory="false">
    <adm:synopsis>
      Indicates whether the plugin should be invoked for internal operations.
      Note that any plugin which may be invoked for internal operations should
      be careful to ensure that they do not create any new internal operatons
      that can cause the same plugin to be re-invoked.
    </adm:synopsis>
    <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.495</ldap:oid>
        <ldap:name>ds-cfg-invoke-for-internal-operations</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/PluginRootConfiguration.xml
@@ -1212,6 +1212,123 @@
    </adm:profile>
  </adm:property>
  <adm:property name="plugin-order-post-synchronization-add" mandatory="false">
    <adm:synopsis>
      Specifies the order in which post-synchronization add plugins should be
      loaded and invoked.  The value should be a comma-delimited list of plugin
      names (where the plugin name is the RDN value from the plugin
      configuration entry DN).  The list may include at most one asterisk to
      indicate the position of any unspecified plugin (and the relative order of
      those unspecified plugins will be undefined).
    </adm:synopsis>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          The order in which post-synchronization add plugins are loaded and
          invoked will be undefined.
        </adm:synopsis>
      </adm:alias>
    </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.491</ldap:oid>
        <ldap:name>ds-cfg-plugin-order-post-synchronization-add</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="plugin-order-post-synchronization-delete"
    mandatory="false">
    <adm:synopsis>
      Specifies the order in which post-synchronization delete plugins should be
      loaded and invoked.  The value should be a comma-delimited list of plugin
      names (where the plugin name is the RDN value from the plugin
      configuration entry DN).  The list may include at most one asterisk to
      indicate the position of any unspecified plugin (and the relative order of
      those unspecified plugins will be undefined).
    </adm:synopsis>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          The order in which post-synchronization delete plugins are loaded and
          invoked will be undefined.
        </adm:synopsis>
      </adm:alias>
    </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.492</ldap:oid>
        <ldap:name>ds-cfg-plugin-order-post-synchronization-delete</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="plugin-order-post-synchronization-modify"
    mandatory="false">
    <adm:synopsis>
      Specifies the order in which post-synchronization modify plugins should be
      loaded and invoked.  The value should be a comma-delimited list of plugin
      names (where the plugin name is the RDN value from the plugin
      configuration entry DN).  The list may include at most one asterisk to
      indicate the position of any unspecified plugin (and the relative order of
      those unspecified plugins will be undefined).
    </adm:synopsis>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          The order in which post-synchronization modify plugins are loaded and
          invoked will be undefined.
        </adm:synopsis>
      </adm:alias>
    </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.493</ldap:oid>
        <ldap:name>ds-cfg-plugin-order-post-synchronization-modify</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="plugin-order-post-synchronization-modify-dn"
    mandatory="false">
    <adm:synopsis>
      Specifies the order in which post-synchronization modify DN plugins should
      be loaded and invoked.  The value should be a comma-delimited list of
      plugin names (where the plugin name is the RDN value from the plugin
      configuration entry DN).  The list may include at most one asterisk to
      indicate the position of any unspecified plugin (and the relative order of
      those unspecified plugins will be undefined).
    </adm:synopsis>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          The order in which post-synchronization modify DN plugins are loaded
          and invoked will be undefined.
        </adm:synopsis>
      </adm:alias>
    </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.494</ldap:oid>
        <ldap:name>
          ds-cfg-plugin-order-post-synchronization-modify-dn
        </ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="plugin-order-post-response-search" mandatory="false">
    <adm:synopsis>
      Specifies the order in which post-response search plugins should be
opends/src/admin/defn/org/opends/server/admin/std/UniqueAttributePluginConfiguration.xml
@@ -63,6 +63,9 @@
        <adm:value>preoperationadd</adm:value>
        <adm:value>preoperationmodify</adm:value>
        <adm:value>preoperationmodifydn</adm:value>
        <adm:value>postsynchronizationadd</adm:value>
        <adm:value>postsynchronizationmodify</adm:value>
        <adm:value>postsynchronizationmodifydn</adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
opends/src/messages/messages/plugin.properties
@@ -379,3 +379,19 @@
SEVERE_ERR_PLUGIN_PWIMPORT_NO_SUCH_DEFAULT_AUTH_SCHEME_104=The password \
 policy import plugin references default auth password storage scheme %s \
 which is not available for use in the server
SEVERE_ERR_PLUGIN_POST_SYNCHRONIZATION_PLUGIN_EXCEPTION_105=The \
 post-synchronization %s plugin defined in configuration entry %s threw an \
 exception when it was invoked for connection %d operation %d:  %s
SEVERE_ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE_106=A unique attribute conflict \
 was detected for attribute %s:  value %s already exists in entry %s
SEVERE_ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE_107=A unique attribute conflict \
 was detected for attribute %s during synchronization (connID=%d, opID=%d):  \
 value %s in entry %s conflicts with an existing value in entry %s.  Manual \
 interaction is required to eliminate the conflict
SEVERE_ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_108=An internal error occurred \
 while attempting to determine whether the operation would have resulted in a \
 unique attribute conflict (result %s, message %s)
SEVERE_ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC_109=An internal error \
 occurred while attempting to determine whether the synchronization operation \
 (connID=%d, opID=%d) for entry %s would have resulted in a unique attribute \
 conflict (result %s, message %s)
opends/src/server/org/opends/server/api/plugin/DirectoryServerPlugin.java
@@ -67,6 +67,10 @@
public abstract class DirectoryServerPlugin
       <T extends PluginCfg>
{
  // Indicates whether this plugin should be invoked for internal
  // operations.
  private boolean invokeForInternalOps;
  // The DN of the configuration entry for this plugin.
  private DN pluginDN;
@@ -119,20 +123,23 @@
   * plugins regardless of type.  This should only be called by the
   * core Directory Server code during the course of loading a plugin.
   *
   * @param  pluginDN     The DN of the plugin configuration entry.
   * @param  pluginTypes  The set of plugin types for which this
   *                      plugin is registered.
   * @param  configuration  The configuration for this plugin.
   * @param  pluginTypes    The set of plugin types for which this
   *                        plugin is registered.
   */
 @org.opends.server.types.PublicAPI(
      stability=org.opends.server.types.StabilityLevel.PRIVATE,
      mayInstantiate=false,
      mayExtend=false,
      mayInvoke=false)
  public final void initializeInternal(DN pluginDN,
  @org.opends.server.types.PublicAPI(
       stability=org.opends.server.types.StabilityLevel.PRIVATE,
       mayInstantiate=false,
       mayExtend=false,
       mayInvoke=false)
  public final void initializeInternal(PluginCfg configuration,
                                       Set<PluginType> pluginTypes)
  {
    this.pluginDN    = pluginDN;
    this.pluginTypes = pluginTypes;
    pluginDN = configuration.dn();
    invokeForInternalOps =
         configuration.isInvokeForInternalOperations();
  }
@@ -198,6 +205,41 @@
  /**
   * Indicates whether this plugin should be invoked for internal
   * operations.
   *
   * @return  {@code true} if this plugin should be invoked for
   *          internal operations, or {@code false} if not.
   */
  public final boolean invokeForInternalOperations()
  {
    return invokeForInternalOps;
  }
  /**
   * Specifies whether this plugin should be invoked for internal
   * operations.
   *
   * @param  invokeForInternalOps  Indicates whether this plugin
   *                               should be invoked for internal
   *                               operations.
   */
  @org.opends.server.types.PublicAPI(
       stability=org.opends.server.types.StabilityLevel.PRIVATE,
       mayInstantiate=false,
       mayExtend=false,
       mayInvoke=false)
  public final void setInvokeForInternalOperations(
                         boolean invokeForInternalOps)
  {
    this.invokeForInternalOps = invokeForInternalOps;
  }
  /**
   * Performs any processing that should be done when the Directory
   * Server is in the process of starting.  This method will be called
   * after virtually all other initialization has been performed but
@@ -448,6 +490,25 @@
  /**
   * Performs any necessary processing that should be done after the
   * Directory Server has completed processing for an add operation
   * performed via synchronization.
   *
   * @param  addOperation  The synchronized add operation for which
   *                       processing has been completed.
   */
  public void doPostSynchronization(
                   PostSynchronizationAddOperation addOperation)
  {
    Message message = ERR_PLUGIN_TYPE_NOT_SUPPORTED.
        get(String.valueOf(pluginDN),
            PluginType.POST_SYNCHRONIZATION_ADD.getName());
    throw new UnsupportedOperationException(message.toString());
  }
  /**
   * Performs any necessary processing that should be done before the
   * Directory Server parses the elements of a bind request.
   *
@@ -702,6 +763,25 @@
  /**
   * Performs any necessary processing that should be done after the
   * Directory Server has completed processing for a delete operation
   * performed via synchronization.
   *
   * @param  deleteOperation  The synchronized delete operation for
   *                          which processing has been completed.
   */
  public void doPostSynchronization(
                   PostSynchronizationDeleteOperation deleteOperation)
  {
    Message message = ERR_PLUGIN_TYPE_NOT_SUPPORTED.
        get(String.valueOf(pluginDN),
            PluginType.POST_SYNCHRONIZATION_DELETE.getName());
    throw new UnsupportedOperationException(message.toString());
  }
  /**
   * Performs any necessary processing that should be done before the
   * Directory Server parses the elements of an extended request.
   *
@@ -875,6 +955,25 @@
  /**
   * Performs any necessary processing that should be done after the
   * Directory Server has completed processing for a modify operation
   * performed via synchronization.
   *
   * @param  modifyOperation  The synchronized modify operation for
   *                          which processing has been completed.
   */
  public void doPostSynchronization(
                   PostSynchronizationModifyOperation modifyOperation)
  {
    Message message = ERR_PLUGIN_TYPE_NOT_SUPPORTED.
        get(String.valueOf(pluginDN),
            PluginType.POST_SYNCHRONIZATION_MODIFY.getName());
    throw new UnsupportedOperationException(message.toString());
  }
  /**
   * Performs any necessary processing that should be done before the
   * Directory Server parses the elements of a modify DN request.
   *
@@ -1003,6 +1102,26 @@
  /**
   * Performs any necessary processing that should be done after the
   * Directory Server has completed processing for a modify DN
   * operation performed via synchronization.
   *
   * @param  modifyDNOperation  The synchronized modify DN operation
   *                            for which processing has been
   *                            completed.
   */
  public void doPostSynchronization(
              PostSynchronizationModifyDNOperation modifyDNOperation)
  {
    Message message = ERR_PLUGIN_TYPE_NOT_SUPPORTED.
        get(String.valueOf(pluginDN),
            PluginType.POST_SYNCHRONIZATION_MODIFY_DN.getName());
    throw new UnsupportedOperationException(message.toString());
  }
  /**
   * Performs any necessary processing that should be done before the
   * Directory Server parses the elements of a search request.
   *
opends/src/server/org/opends/server/api/plugin/PluginType.java
@@ -383,6 +383,41 @@
  /**
   * The plugin type for plugins that are to be invoked just after an
   * add operation has been completed via synchronization.
   */
  POST_SYNCHRONIZATION_ADD(PluginType.NAME_POST_SYNCHRONIZATION_ADD),
  /**
   * The plugin type for plugins that are to be invoked just after a
   * delete operation has been completed via synchronization.
   */
  POST_SYNCHRONIZATION_DELETE(
       PluginType.NAME_POST_SYNCHRONIZATION_DELETE),
  /**
   * The plugin type for plugins that are to be invoked just after a
   * modify operation has been completed via synchronization.
   */
  POST_SYNCHRONIZATION_MODIFY(
       PluginType.NAME_POST_SYNCHRONIZATION_MODIFY),
  /**
   * The plugin type for plugins that are to be invoked just after a
   * modify DN operation has been completed via synchronization.
   */
  POST_SYNCHRONIZATION_MODIFY_DN(
       PluginType.NAME_POST_SYNCHRONIZATION_MODIFY_DN),
  /**
   * The plugin type for plugins that are to be invoked before each
   * search result entry is sent to a client.
   */
@@ -744,6 +779,41 @@
  /**
   * The name that will be used for post-synchronization add plugins.
   */
  private static final String NAME_POST_SYNCHRONIZATION_ADD =
       "postsynchronizationadd";
  /**
   * The name that will be used for post-synchronization delete
   * plugins.
   */
  private static final String NAME_POST_SYNCHRONIZATION_DELETE =
       "postsynchronizationdelete";
  /**
   * The name that will be used for post-synchronization modify
   * plugins.
   */
  private static final String NAME_POST_SYNCHRONIZATION_MODIFY =
       "postsynchronizationmodify";
  /**
   * The name that will be used for post-synchronization modify DN
   * plugins.
   */
  private static final String NAME_POST_SYNCHRONIZATION_MODIFY_DN =
       "postsynchronizationmodifydn";
  /**
   * The name that will be used for search result entry plugins.
   */
  private static final String NAME_SEARCH_ENTRY = "searchresultentry";
@@ -779,7 +849,7 @@
   * types.
   */
  private static final Set<String> PLUGIN_TYPE_NAMES =
       new HashSet<String>(46);
       new HashSet<String>(50);
@@ -788,7 +858,7 @@
   * corresponding plugin type.
   */
  private static final Map<String,PluginType> PLUGIN_TYPE_MAP =
       new HashMap<String,PluginType>(46);
       new HashMap<String,PluginType>(50);
@@ -836,6 +906,13 @@
    PLUGIN_TYPE_NAMES.add(PluginType.NAME_POST_RESPONSE_MODIFY);
    PLUGIN_TYPE_NAMES.add(PluginType.NAME_POST_RESPONSE_MODIFY_DN);
    PLUGIN_TYPE_NAMES.add(PluginType.NAME_POST_RESPONSE_SEARCH);
    PLUGIN_TYPE_NAMES.add(PluginType.NAME_POST_SYNCHRONIZATION_ADD);
    PLUGIN_TYPE_NAMES.add(
         PluginType.NAME_POST_SYNCHRONIZATION_DELETE);
    PLUGIN_TYPE_NAMES.add(
         PluginType.NAME_POST_SYNCHRONIZATION_MODIFY);
    PLUGIN_TYPE_NAMES.add(
         PluginType.NAME_POST_SYNCHRONIZATION_MODIFY_DN);
    PLUGIN_TYPE_NAMES.add(PluginType.NAME_SEARCH_ENTRY);
    PLUGIN_TYPE_NAMES.add(PluginType.NAME_SEARCH_REFERENCE);
    PLUGIN_TYPE_NAMES.add(PluginType.NAME_SUBORDINATE_MODIFY_DN);
@@ -924,6 +1001,15 @@
                        PluginType.POST_RESPONSE_MODIFY_DN);
    PLUGIN_TYPE_MAP.put(PluginType.NAME_POST_RESPONSE_SEARCH,
                        PluginType.POST_RESPONSE_SEARCH);
    PLUGIN_TYPE_MAP.put(PluginType.NAME_POST_SYNCHRONIZATION_ADD,
                        PluginType.POST_SYNCHRONIZATION_ADD);
    PLUGIN_TYPE_MAP.put(PluginType.NAME_POST_SYNCHRONIZATION_DELETE,
                        PluginType.POST_SYNCHRONIZATION_DELETE);
    PLUGIN_TYPE_MAP.put(PluginType.NAME_POST_SYNCHRONIZATION_MODIFY,
                        PluginType.POST_SYNCHRONIZATION_MODIFY);
    PLUGIN_TYPE_MAP.put(
         PluginType.NAME_POST_SYNCHRONIZATION_MODIFY_DN,
         PluginType.POST_SYNCHRONIZATION_MODIFY_DN);
    PLUGIN_TYPE_MAP.put(PluginType.NAME_SEARCH_ENTRY,
                        PluginType.SEARCH_RESULT_ENTRY);
    PLUGIN_TYPE_MAP.put(PluginType.NAME_SEARCH_REFERENCE,
opends/src/server/org/opends/server/core/PluginConfigManager.java
@@ -25,7 +25,6 @@
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.core;
import org.opends.messages.Message;
@@ -41,6 +40,8 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.admin.ClassPropertyDefinition;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationChangeListener;
@@ -66,12 +67,11 @@
import org.opends.server.api.plugin.StartupPluginResult;
import org.opends.server.api.plugin.SubordinateModifyDNPluginResult;
import org.opends.server.config.ConfigException;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.IntermediateResponse;
import org.opends.server.types.LDIFExportConfig;
@@ -80,7 +80,6 @@
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchResultReference;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.Modification;
import org.opends.server.types.operation.PostOperationAbandonOperation;
@@ -101,6 +100,10 @@
import org.opends.server.types.operation.PostResponseModifyDNOperation;
import org.opends.server.types.operation.PostResponseModifyOperation;
import org.opends.server.types.operation.PostResponseSearchOperation;
import org.opends.server.types.operation.PostSynchronizationAddOperation;
import org.opends.server.types.operation.PostSynchronizationDeleteOperation;
import org.opends.server.types.operation.PostSynchronizationModifyOperation;
import org.opends.server.types.operation.PostSynchronizationModifyDNOperation;
import org.opends.server.types.operation.PreOperationAddOperation;
import org.opends.server.types.operation.PreOperationBindOperation;
import org.opends.server.types.operation.PreOperationCompareOperation;
@@ -124,13 +127,10 @@
import org.opends.server.types.operation.SubordinateModifyDNOperation;
import org.opends.server.workflowelement.localbackend.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.loggers.ErrorLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.messages.PluginMessages.*;
import org.opends.messages.MessageBuilder;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.loggers.ErrorLogger.*;
import static org.opends.server.util.StaticUtils.*;
@@ -195,6 +195,10 @@
  private DirectoryServerPlugin[] postResponseModifyPlugins;
  private DirectoryServerPlugin[] postResponseModifyDNPlugins;
  private DirectoryServerPlugin[] postResponseSearchPlugins;
  private DirectoryServerPlugin[] postSynchronizationAddPlugins;
  private DirectoryServerPlugin[] postSynchronizationDeletePlugins;
  private DirectoryServerPlugin[] postSynchronizationModifyPlugins;
  private DirectoryServerPlugin[] postSynchronizationModifyDNPlugins;
  private DirectoryServerPlugin[] searchResultEntryPlugins;
  private DirectoryServerPlugin[] searchResultReferencePlugins;
  private DirectoryServerPlugin[] subordinateModifyDNPlugins;
@@ -223,53 +227,57 @@
  {
    pluginLock = new ReentrantLock();
    startupPlugins               = new DirectoryServerPlugin[0];
    shutdownPlugins              = new DirectoryServerPlugin[0];
    postConnectPlugins           = new DirectoryServerPlugin[0];
    postDisconnectPlugins        = new DirectoryServerPlugin[0];
    ldifImportPlugins            = new DirectoryServerPlugin[0];
    ldifExportPlugins            = new DirectoryServerPlugin[0];
    preParseAbandonPlugins       = new DirectoryServerPlugin[0];
    preParseAddPlugins           = new DirectoryServerPlugin[0];
    preParseBindPlugins          = new DirectoryServerPlugin[0];
    preParseComparePlugins       = new DirectoryServerPlugin[0];
    preParseDeletePlugins        = new DirectoryServerPlugin[0];
    preParseExtendedPlugins      = new DirectoryServerPlugin[0];
    preParseModifyPlugins        = new DirectoryServerPlugin[0];
    preParseModifyDNPlugins      = new DirectoryServerPlugin[0];
    preParseSearchPlugins        = new DirectoryServerPlugin[0];
    preParseUnbindPlugins        = new DirectoryServerPlugin[0];
    preOperationAddPlugins       = new DirectoryServerPlugin[0];
    preOperationBindPlugins      = new DirectoryServerPlugin[0];
    preOperationComparePlugins   = new DirectoryServerPlugin[0];
    preOperationDeletePlugins    = new DirectoryServerPlugin[0];
    preOperationExtendedPlugins  = new DirectoryServerPlugin[0];
    preOperationModifyPlugins    = new DirectoryServerPlugin[0];
    preOperationModifyDNPlugins  = new DirectoryServerPlugin[0];
    preOperationSearchPlugins    = new DirectoryServerPlugin[0];
    postOperationAbandonPlugins  = new DirectoryServerPlugin[0];
    postOperationAddPlugins      = new DirectoryServerPlugin[0];
    postOperationBindPlugins     = new DirectoryServerPlugin[0];
    postOperationComparePlugins  = new DirectoryServerPlugin[0];
    postOperationDeletePlugins   = new DirectoryServerPlugin[0];
    postOperationExtendedPlugins = new DirectoryServerPlugin[0];
    postOperationModifyPlugins   = new DirectoryServerPlugin[0];
    postOperationModifyDNPlugins = new DirectoryServerPlugin[0];
    postOperationSearchPlugins   = new DirectoryServerPlugin[0];
    postOperationUnbindPlugins   = new DirectoryServerPlugin[0];
    postResponseAddPlugins       = new DirectoryServerPlugin[0];
    postResponseBindPlugins      = new DirectoryServerPlugin[0];
    postResponseComparePlugins   = new DirectoryServerPlugin[0];
    postResponseDeletePlugins    = new DirectoryServerPlugin[0];
    postResponseExtendedPlugins  = new DirectoryServerPlugin[0];
    postResponseModifyPlugins    = new DirectoryServerPlugin[0];
    postResponseModifyDNPlugins  = new DirectoryServerPlugin[0];
    postResponseSearchPlugins    = new DirectoryServerPlugin[0];
    searchResultEntryPlugins     = new DirectoryServerPlugin[0];
    searchResultReferencePlugins = new DirectoryServerPlugin[0];
    subordinateModifyDNPlugins   = new DirectoryServerPlugin[0];
    intermediateResponsePlugins  = new DirectoryServerPlugin[0];
    registeredPlugins            =
    startupPlugins                     = new DirectoryServerPlugin[0];
    shutdownPlugins                    = new DirectoryServerPlugin[0];
    postConnectPlugins                 = new DirectoryServerPlugin[0];
    postDisconnectPlugins              = new DirectoryServerPlugin[0];
    ldifImportPlugins                  = new DirectoryServerPlugin[0];
    ldifExportPlugins                  = new DirectoryServerPlugin[0];
    preParseAbandonPlugins             = new DirectoryServerPlugin[0];
    preParseAddPlugins                 = new DirectoryServerPlugin[0];
    preParseBindPlugins                = new DirectoryServerPlugin[0];
    preParseComparePlugins             = new DirectoryServerPlugin[0];
    preParseDeletePlugins              = new DirectoryServerPlugin[0];
    preParseExtendedPlugins            = new DirectoryServerPlugin[0];
    preParseModifyPlugins              = new DirectoryServerPlugin[0];
    preParseModifyDNPlugins            = new DirectoryServerPlugin[0];
    preParseSearchPlugins              = new DirectoryServerPlugin[0];
    preParseUnbindPlugins              = new DirectoryServerPlugin[0];
    preOperationAddPlugins             = new DirectoryServerPlugin[0];
    preOperationBindPlugins            = new DirectoryServerPlugin[0];
    preOperationComparePlugins         = new DirectoryServerPlugin[0];
    preOperationDeletePlugins          = new DirectoryServerPlugin[0];
    preOperationExtendedPlugins        = new DirectoryServerPlugin[0];
    preOperationModifyPlugins          = new DirectoryServerPlugin[0];
    preOperationModifyDNPlugins        = new DirectoryServerPlugin[0];
    preOperationSearchPlugins          = new DirectoryServerPlugin[0];
    postOperationAbandonPlugins        = new DirectoryServerPlugin[0];
    postOperationAddPlugins            = new DirectoryServerPlugin[0];
    postOperationBindPlugins           = new DirectoryServerPlugin[0];
    postOperationComparePlugins        = new DirectoryServerPlugin[0];
    postOperationDeletePlugins         = new DirectoryServerPlugin[0];
    postOperationExtendedPlugins       = new DirectoryServerPlugin[0];
    postOperationModifyPlugins         = new DirectoryServerPlugin[0];
    postOperationModifyDNPlugins       = new DirectoryServerPlugin[0];
    postOperationSearchPlugins         = new DirectoryServerPlugin[0];
    postOperationUnbindPlugins         = new DirectoryServerPlugin[0];
    postResponseAddPlugins             = new DirectoryServerPlugin[0];
    postResponseBindPlugins            = new DirectoryServerPlugin[0];
    postResponseComparePlugins         = new DirectoryServerPlugin[0];
    postResponseDeletePlugins          = new DirectoryServerPlugin[0];
    postResponseExtendedPlugins        = new DirectoryServerPlugin[0];
    postResponseModifyPlugins          = new DirectoryServerPlugin[0];
    postResponseModifyDNPlugins        = new DirectoryServerPlugin[0];
    postResponseSearchPlugins          = new DirectoryServerPlugin[0];
    postSynchronizationAddPlugins      = new DirectoryServerPlugin[0];
    postSynchronizationDeletePlugins   = new DirectoryServerPlugin[0];
    postSynchronizationModifyPlugins   = new DirectoryServerPlugin[0];
    postSynchronizationModifyDNPlugins = new DirectoryServerPlugin[0];
    searchResultEntryPlugins           = new DirectoryServerPlugin[0];
    searchResultReferencePlugins       = new DirectoryServerPlugin[0];
    subordinateModifyDNPlugins         = new DirectoryServerPlugin[0];
    intermediateResponsePlugins        = new DirectoryServerPlugin[0];
    registeredPlugins                  =
         new ConcurrentHashMap<DN,
                  DirectoryServerPlugin<? extends PluginCfg>>();
  }
@@ -404,8 +412,8 @@
      if (initialize)
      {
        Method method = plugin.getClass().getMethod("initializeInternal",
                                                    DN.class, Set.class);
        method.invoke(plugin, configuration.dn(), pluginTypes);
                                                    PluginCfg.class, Set.class);
        method.invoke(plugin, configuration, pluginTypes);
        method = plugin.getClass().getMethod("initializePlugin", Set.class,
                      configuration.definition().getServerConfigurationClass());
@@ -511,6 +519,14 @@
      case SEARCHRESULTREFERENCE:  return PluginType.SEARCH_RESULT_REFERENCE;
      case SUBORDINATEMODIFYDN:    return PluginType.SUBORDINATE_MODIFY_DN;
      case INTERMEDIATERESPONSE:   return PluginType.INTERMEDIATE_RESPONSE;
      case POSTSYNCHRONIZATIONADD:
                return PluginType.POST_SYNCHRONIZATION_ADD;
      case POSTSYNCHRONIZATIONDELETE:
                return PluginType.POST_SYNCHRONIZATION_DELETE;
      case POSTSYNCHRONIZATIONMODIFY:
                return PluginType.POST_SYNCHRONIZATION_MODIFY;
      case POSTSYNCHRONIZATIONMODIFYDN:
                return PluginType.POST_SYNCHRONIZATION_MODIFY_DN;
      default:                     return null;
    }
  }
@@ -819,6 +835,30 @@
                 addPlugin(postResponseSearchPlugins, plugin, t,
                           pluginRootConfig.getPluginOrderPostResponseSearch());
            break;
          case POST_SYNCHRONIZATION_ADD:
            postSynchronizationAddPlugins =
                 addPlugin(postSynchronizationAddPlugins, plugin, t,
                           pluginRootConfig.
                                getPluginOrderPostSynchronizationAdd());
            break;
          case POST_SYNCHRONIZATION_DELETE:
            postSynchronizationDeletePlugins =
                 addPlugin(postSynchronizationDeletePlugins, plugin, t,
                           pluginRootConfig.
                                getPluginOrderPostSynchronizationDelete());
            break;
          case POST_SYNCHRONIZATION_MODIFY:
            postSynchronizationModifyPlugins =
                 addPlugin(postSynchronizationModifyPlugins, plugin, t,
                           pluginRootConfig.
                                getPluginOrderPostSynchronizationModify());
            break;
          case POST_SYNCHRONIZATION_MODIFY_DN:
            postSynchronizationModifyDNPlugins =
                 addPlugin(postSynchronizationModifyDNPlugins, plugin, t,
                           pluginRootConfig.
                                getPluginOrderPostSynchronizationModifyDN());
            break;
          case SEARCH_RESULT_ENTRY:
            searchResultEntryPlugins =
                 addPlugin(searchResultEntryPlugins, plugin, t,
@@ -1244,6 +1284,22 @@
            postResponseSearchPlugins = removePlugin(postResponseSearchPlugins,
                                                     plugin);
            break;
          case POST_SYNCHRONIZATION_ADD:
            postSynchronizationAddPlugins =
                 removePlugin(postSynchronizationAddPlugins, plugin);
            break;
          case POST_SYNCHRONIZATION_DELETE:
            postSynchronizationDeletePlugins =
                 removePlugin(postSynchronizationDeletePlugins, plugin);
            break;
          case POST_SYNCHRONIZATION_MODIFY:
            postSynchronizationModifyPlugins =
                 removePlugin(postSynchronizationModifyPlugins, plugin);
            break;
          case POST_SYNCHRONIZATION_MODIFY_DN:
            postSynchronizationModifyDNPlugins =
                 removePlugin(postSynchronizationModifyDNPlugins, plugin);
            break;
          case SEARCH_RESULT_ENTRY:
            searchResultEntryPlugins = removePlugin(searchResultEntryPlugins,
                                                    plugin);
@@ -1740,6 +1796,12 @@
    for (DirectoryServerPlugin p : preParseAbandonPlugins)
    {
      if (abandonOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreParse(abandonOperation);
@@ -1816,6 +1878,12 @@
    for (DirectoryServerPlugin p : preParseAddPlugins)
    {
      if (addOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreParse(addOperation);
@@ -1889,6 +1957,12 @@
    for (DirectoryServerPlugin p : preParseBindPlugins)
    {
      if (bindOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreParse(bindOperation);
@@ -1962,6 +2036,12 @@
    for (DirectoryServerPlugin p : preParseComparePlugins)
    {
      if (compareOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreParse(compareOperation);
@@ -2038,6 +2118,12 @@
    for (DirectoryServerPlugin p : preParseDeletePlugins)
    {
      if (deleteOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreParse(deleteOperation);
@@ -2114,6 +2200,12 @@
    for (DirectoryServerPlugin p : preParseExtendedPlugins)
    {
      if (extendedOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreParse(extendedOperation);
@@ -2178,21 +2270,27 @@
   * Invokes the set of pre-parse modify plugins that have been configured in
   * the Directory Server.
   *
   * @param  operation  The modify operation for which to invoke the
   * @param  modifyOperation  The modify operation for which to invoke the
   *                          pre-parse plugins.
   *
   * @return  The result of processing the pre-parse modify plugins.
   */
  public PreParsePluginResult invokePreParseModifyPlugins(
                                   PreParseModifyOperation operation)
                                   PreParseModifyOperation modifyOperation)
  {
    PreParsePluginResult result = null;
    for (DirectoryServerPlugin p : preParseModifyPlugins)
    {
      if (modifyOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreParse(operation);
        result = p.doPreParse(modifyOperation);
      }
      catch (Exception e)
      {
@@ -2202,15 +2300,16 @@
        }
        Message message = ERR_PLUGIN_PRE_PARSE_PLUGIN_EXCEPTION.
            get(operation.getOperationType().getOperationName(),
            get(modifyOperation.getOperationType().getOperationName(),
                String.valueOf(p.getPluginEntryDN()),
                operation.getConnectionID(), operation.getOperationID(),
                modifyOperation.getConnectionID(),
                modifyOperation.getOperationID(),
                stackTraceToSingleLineString(e));
        logError(message);
        operation.setResultCode(
        modifyOperation.setResultCode(
                             DirectoryServer.getServerErrorResultCode());
        operation.appendErrorMessage(message);
        modifyOperation.appendErrorMessage(message);
        return new PreParsePluginResult(false, false, true);
      }
@@ -2218,15 +2317,15 @@
      if (result == null)
      {
        Message message = ERR_PLUGIN_PRE_PARSE_PLUGIN_RETURNED_NULL.
            get(operation.getOperationType().getOperationName(),
            get(modifyOperation.getOperationType().getOperationName(),
                String.valueOf(p.getPluginEntryDN()),
                operation.getConnectionID(),
                String.valueOf(operation.getOperationID()));
                modifyOperation.getConnectionID(),
                String.valueOf(modifyOperation.getOperationID()));
        logError(message);
        operation.setResultCode(
        modifyOperation.setResultCode(
                             DirectoryServer.getServerErrorResultCode());
        operation.appendErrorMessage(message);
        modifyOperation.appendErrorMessage(message);
        return new PreParsePluginResult(false, false, true);
      }
@@ -2265,6 +2364,12 @@
    for (DirectoryServerPlugin p : preParseModifyDNPlugins)
    {
      if (modifyDNOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreParse(modifyDNOperation);
@@ -2341,6 +2446,12 @@
    for (DirectoryServerPlugin p : preParseSearchPlugins)
    {
      if (searchOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreParse(searchOperation);
@@ -2417,6 +2528,12 @@
    for (DirectoryServerPlugin p : preParseUnbindPlugins)
    {
      if (unbindOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreParse(unbindOperation);
@@ -2493,6 +2610,12 @@
    for (DirectoryServerPlugin p : preOperationAddPlugins)
    {
      if (addOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreOperation(addOperation);
@@ -2565,6 +2688,12 @@
    for (DirectoryServerPlugin p : preOperationBindPlugins)
    {
      if (bindOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreOperation(bindOperation);
@@ -2638,6 +2767,12 @@
    for (DirectoryServerPlugin p : preOperationComparePlugins)
    {
      if (compareOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreOperation(compareOperation);
@@ -2714,6 +2849,12 @@
    for (DirectoryServerPlugin p : preOperationDeletePlugins)
    {
      if (deleteOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreOperation(deleteOperation);
@@ -2790,6 +2931,12 @@
    for (DirectoryServerPlugin p : preOperationExtendedPlugins)
    {
      if (extendedOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreOperation(extendedOperation);
@@ -2866,6 +3013,12 @@
    for (DirectoryServerPlugin p : preOperationModifyPlugins)
    {
      if (modifyOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreOperation(modifyOperation);
@@ -2942,6 +3095,12 @@
    for (DirectoryServerPlugin p : preOperationModifyDNPlugins)
    {
      if (modifyDNOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreOperation(modifyDNOperation);
@@ -3018,6 +3177,12 @@
    for (DirectoryServerPlugin p : preOperationSearchPlugins)
    {
      if (searchOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPreOperation(searchOperation);
@@ -3094,6 +3259,12 @@
    for (DirectoryServerPlugin p : postOperationAbandonPlugins)
    {
      if (abandonOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostOperation(abandonOperation);
@@ -3170,6 +3341,12 @@
    for (DirectoryServerPlugin p : postOperationAddPlugins)
    {
      if (addOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostOperation(addOperation);
@@ -3242,6 +3419,12 @@
    for (DirectoryServerPlugin p : postOperationBindPlugins)
    {
      if (bindOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostOperation(bindOperation);
@@ -3315,6 +3498,12 @@
    for (DirectoryServerPlugin p : postOperationComparePlugins)
    {
      if (compareOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostOperation(compareOperation);
@@ -3391,6 +3580,12 @@
    for (DirectoryServerPlugin p : postOperationDeletePlugins)
    {
      if (deleteOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostOperation(deleteOperation);
@@ -3467,6 +3662,12 @@
    for (DirectoryServerPlugin p : postOperationExtendedPlugins)
    {
      if (extendedOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostOperation(extendedOperation);
@@ -3543,6 +3744,12 @@
    for (DirectoryServerPlugin p : postOperationModifyPlugins)
    {
      if (modifyOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostOperation(modifyOperation);
@@ -3619,6 +3826,12 @@
    for (DirectoryServerPlugin p : postOperationModifyDNPlugins)
    {
      if (modifyDNOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostOperation(modifyDNOperation);
@@ -3695,6 +3908,12 @@
    for (DirectoryServerPlugin p : postOperationSearchPlugins)
    {
      if (searchOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostOperation(searchOperation);
@@ -3771,6 +3990,12 @@
    for (DirectoryServerPlugin p : postOperationUnbindPlugins)
    {
      if (unbindOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostOperation(unbindOperation);
@@ -3847,6 +4072,12 @@
    for (DirectoryServerPlugin p : postResponseAddPlugins)
    {
      if (addOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostResponse(addOperation);
@@ -3913,6 +4144,12 @@
    for (DirectoryServerPlugin p : postResponseBindPlugins)
    {
      if (bindOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostResponse(bindOperation);
@@ -3980,6 +4217,12 @@
    for (DirectoryServerPlugin p : postResponseComparePlugins)
    {
      if (compareOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostResponse(compareOperation);
@@ -4048,6 +4291,12 @@
    for (DirectoryServerPlugin p : postResponseDeletePlugins)
    {
      if (deleteOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostResponse(deleteOperation);
@@ -4116,6 +4365,12 @@
    for (DirectoryServerPlugin p : postResponseExtendedPlugins)
    {
      if (extendedOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostResponse(extendedOperation);
@@ -4184,6 +4439,12 @@
    for (DirectoryServerPlugin p : postResponseModifyPlugins)
    {
      if (modifyOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostResponse(modifyOperation);
@@ -4252,6 +4513,12 @@
    for (DirectoryServerPlugin p : postResponseModifyDNPlugins)
    {
      if (modifyDNOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostResponse(modifyDNOperation);
@@ -4320,6 +4587,12 @@
    for (DirectoryServerPlugin p : postResponseSearchPlugins)
    {
      if (searchOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.doPostResponse(searchOperation);
@@ -4370,6 +4643,151 @@
    return result;
  }
  /**
   * Invokes the set of post-synchronization add plugins that have been
   * configured in the Directory Server.
   *
   * @param  addOperation  The add operation for which to invoke the
   *                       post-synchronization plugins.
   */
  public void invokePostSynchronizationAddPlugins(
                   PostSynchronizationAddOperation addOperation)
  {
    for (DirectoryServerPlugin p : postSynchronizationAddPlugins)
    {
      try
      {
        p.doPostSynchronization(addOperation);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        Message message = ERR_PLUGIN_POST_SYNCHRONIZATION_PLUGIN_EXCEPTION.
            get(addOperation.getOperationType().getOperationName(),
                String.valueOf(p.getPluginEntryDN()),
                addOperation.getConnectionID(), addOperation.getOperationID(),
                stackTraceToSingleLineString(e));
        logError(message);
      }
    }
  }
  /**
   * Invokes the set of post-synchronization delete plugins that have been
   * configured in the Directory Server.
   *
   * @param  deleteOperation  The delete operation for which to invoke the
   *                          post-synchronization plugins.
   */
  public void invokePostSynchronizationDeletePlugins(
                   PostSynchronizationDeleteOperation deleteOperation)
  {
    for (DirectoryServerPlugin p : postSynchronizationDeletePlugins)
    {
      try
      {
        p.doPostSynchronization(deleteOperation);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        Message message = ERR_PLUGIN_POST_SYNCHRONIZATION_PLUGIN_EXCEPTION.
            get(deleteOperation.getOperationType().getOperationName(),
                String.valueOf(p.getPluginEntryDN()),
                deleteOperation.getConnectionID(),
                deleteOperation.getOperationID(),
                stackTraceToSingleLineString(e));
        logError(message);
      }
    }
  }
  /**
   * Invokes the set of post-synchronization modify plugins that have been
   * configured in the Directory Server.
   *
   * @param  modifyOperation  The modify operation for which to invoke the
   *                          post-synchronization plugins.
   */
  public void invokePostSynchronizationModifyPlugins(
                   PostSynchronizationModifyOperation modifyOperation)
  {
    for (DirectoryServerPlugin p : postSynchronizationModifyPlugins)
    {
      try
      {
        p.doPostSynchronization(modifyOperation);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        Message message = ERR_PLUGIN_POST_SYNCHRONIZATION_PLUGIN_EXCEPTION.
            get(modifyOperation.getOperationType().getOperationName(),
                String.valueOf(p.getPluginEntryDN()),
                modifyOperation.getConnectionID(),
                modifyOperation.getOperationID(),
                stackTraceToSingleLineString(e));
        logError(message);
      }
    }
  }
  /**
   * Invokes the set of post-synchronization modify DN plugins that have been
   * configured in the Directory Server.
   *
   * @param  modifyDNOperation  The modify DN operation for which to invoke the
   *                            post-synchronization plugins.
   */
  public void invokePostSynchronizationModifyDNPlugins(
                   PostSynchronizationModifyDNOperation modifyDNOperation)
  {
    for (DirectoryServerPlugin p : postSynchronizationModifyDNPlugins)
    {
      try
      {
        p.doPostSynchronization(modifyDNOperation);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        Message message = ERR_PLUGIN_POST_SYNCHRONIZATION_PLUGIN_EXCEPTION.
            get(modifyDNOperation.getOperationType().getOperationName(),
                String.valueOf(p.getPluginEntryDN()),
                modifyDNOperation.getConnectionID(),
                modifyDNOperation.getOperationID(),
                stackTraceToSingleLineString(e));
        logError(message);
      }
    }
  }
  /**
   * Invokes the set of search result entry plugins that have been configured
   * in the Directory Server.
@@ -4388,6 +4806,12 @@
    for (DirectoryServerPlugin p : searchResultEntryPlugins)
    {
      if (searchOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.processSearchEntry(searchOperation, searchEntry);
@@ -4438,6 +4862,8 @@
    return result;
  }
  /**
   * Invokes the set of search result entry plugins that have been configured
   * in the Directory Server.
@@ -4456,6 +4882,18 @@
    for (DirectoryServerPlugin p : searchResultEntryPlugins)
    {
      if (searchOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      if (searchOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.processSearchEntry(searchOperation, searchEntry);
@@ -4506,6 +4944,8 @@
    return result;
  }
  /**
   * Invokes the set of search result reference plugins that have been
   * configured in the Directory Server.
@@ -4524,6 +4964,12 @@
    for (DirectoryServerPlugin p : searchResultReferencePlugins)
    {
      if (searchOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.processSearchReference(searchOperation, searchReference);
@@ -4574,6 +5020,8 @@
    return result;
  }
  /**
   * Invokes the set of search result reference plugins that have been
   * configured in the Directory Server.
@@ -4592,6 +5040,12 @@
    for (DirectoryServerPlugin p : searchResultReferencePlugins)
    {
      if (searchOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        result = p.processSearchReference(searchOperation, searchReference);
@@ -4667,6 +5121,12 @@
    for (DirectoryServerPlugin p : subordinateModifyDNPlugins)
    {
      if (modifyDNOperation.isInternalOperation() &&
          (! p.invokeForInternalOperations()))
      {
        continue;
      }
      try
      {
        DirectoryServerPlugin<? extends PluginCfg> gp =
@@ -4972,7 +5432,8 @@
    // then we shouldn't do anything with it although if the class has changed
    // then we'll at least need to indicate that administrative action is
    // required.  If the mapper is disabled, then instantiate the class and
    // initialize and register it as an identity mapper.
    // initialize and register it as an identity mapper.  Also, update the
    // plugin to indicate whether it should be invoked for internal operations.
    String className = configuration.getPluginClass();
    if (existingPlugin != null)
    {
@@ -4981,6 +5442,9 @@
        adminActionRequired = true;
      }
      existingPlugin.setInvokeForInternalOperations(
                          configuration.isInvokeForInternalOperations());
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java
@@ -465,6 +465,10 @@
      StringBuilder buffer = new StringBuilder(50);
      buffer.append("# ");
      buffer.append(TimeThread.getLocalTime());
      buffer.append("; conn=");
      buffer.append(addOperation.getConnectionID());
      buffer.append("; op=");
      buffer.append(addOperation.getOperationID());
      buffer.append(EOL);
      buffer.append("dn:");
@@ -598,6 +602,10 @@
      StringBuilder buffer = new StringBuilder(50);
      buffer.append("# ");
      buffer.append(TimeThread.getLocalTime());
      buffer.append("; conn=");
      buffer.append(deleteOperation.getConnectionID());
      buffer.append("; op=");
      buffer.append(deleteOperation.getOperationID());
      buffer.append(EOL);
      buffer.append("dn:");
@@ -676,6 +684,10 @@
      StringBuilder buffer = new StringBuilder(50);
      buffer.append("# ");
      buffer.append(TimeThread.getLocalTime());
      buffer.append("; conn=");
      buffer.append(modifyOperation.getConnectionID());
      buffer.append("; op=");
      buffer.append(modifyOperation.getOperationID());
      buffer.append(EOL);
      buffer.append("dn:");
@@ -777,6 +789,10 @@
      StringBuilder buffer = new StringBuilder(50);
      buffer.append("# ");
      buffer.append(TimeThread.getLocalTime());
      buffer.append("; conn=");
      buffer.append(modifyDNOperation.getConnectionID());
      buffer.append("; op=");
      buffer.append(modifyDNOperation.getOperationID());
      buffer.append(EOL);
      buffer.append("dn:");
opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java
@@ -26,47 +26,104 @@
 */
package org.opends.server.plugins;
import org.opends.server.admin.std.server.UniqueAttributePluginCfg;
import org.opends.server.admin.std.meta.PluginCfgDefn;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.opends.messages.Message;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.meta.PluginCfgDefn;
import org.opends.server.admin.std.server.PluginCfg;
import org.opends.server.admin.std.server.UniqueAttributePluginCfg;
import org.opends.server.api.AlertGenerator;
import org.opends.server.api.plugin.DirectoryServerPlugin;
import org.opends.server.api.plugin.PluginType;
import org.opends.server.api.plugin.PreOperationPluginResult;
import org.opends.server.config.ConfigException;
import org.opends.server.types.*;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DereferencePolicy;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
import org.opends.server.types.RDN;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchScope;
import org.opends.server.types.operation.PostSynchronizationAddOperation;
import org.opends.server.types.operation.PostSynchronizationModifyDNOperation;
import org.opends.server.types.operation.PostSynchronizationModifyOperation;
import org.opends.server.types.operation.PreOperationAddOperation;
import org.opends.server.types.operation.PreOperationModifyDNOperation;
import org.opends.server.types.operation.PreOperationModifyOperation;
import org.opends.server.types.operation.PreOperationOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.messages.Message;
import static org.opends.messages.PluginMessages.*;
import java.util.*;
import static org.opends.messages.PluginMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.ServerConstants.*;
/**
 * This class implements a Directory Server plugin that performs attribute
 * uniqueness checking on the add, modify and modifyDN operations. If the
 * operation is eligible for checking based on a set of configuration criteria,
 * then the operation's attribute values will be checked, using that
 * configuration criteria, for uniqueness against the server's values to
 * determine if the operation can proceed.
 * This class implements a Directory Server plugin that can be used to ensure
 * that all values for a given attribute or set of attributes are unique within
 * the server (or optionally, below a specified set of base DNs).  It will
 * examine all add, modify, and modify DN operations to determine whether any
 * new conflicts are introduced.  If a conflict is detected then the operation
 * will be rejected, unless that operation is being applied through
 * synchronization in which case an alert will be generated to notify
 * administrators of the problem.
 */
public class UniqueAttributePlugin
        extends DirectoryServerPlugin<UniqueAttributePluginCfg>
        implements ConfigurationChangeListener<UniqueAttributePluginCfg> {
        implements ConfigurationChangeListener<UniqueAttributePluginCfg>,
                   AlertGenerator
{
  /**
   * The debug log tracer that will be used for this plugin.
   */
  private static final DebugTracer TRACER = getTracer();
  /**
   * The pre-operation plugin result that should be returned if an operation
   * would have resulted in a unique attribute conflict.
   */
  private static final PreOperationPluginResult FAILED_PREOP_RESULT =
       new PreOperationPluginResult(false, false, false, true);
  /**
   * The set of attributes that will be requested when performing internal
   * search operations.  This indicates that no attributes should be returned.
   */
  private static final LinkedHashSet<String> SEARCH_ATTRS =
       new LinkedHashSet<String>(1);
  static
  {
    SEARCH_ATTRS.add("1.1");
  }
  //Current plugin configuration.
  private UniqueAttributePluginCfg currentConfiguration;
  //List of attribute types that must be unique.
  private LinkedHashSet<AttributeType> uniqueAttributeTypes =
          new LinkedHashSet<AttributeType>();
//List of base DNs that limit the scope of the uniqueness checking.
 private LinkedHashSet<DN> baseDNs = new LinkedHashSet<DN>();
  /**
   * {@inheritDoc}
@@ -74,37 +131,710 @@
  @Override()
  public final void initializePlugin(Set<PluginType> pluginTypes,
                                     UniqueAttributePluginCfg configuration)
          throws ConfigException {
          throws ConfigException
  {
    configuration.addUniqueAttributeChangeListener(this);
    currentConfiguration = configuration;
    DirectoryServer.registerAlertGenerator(this);
    for (PluginType t : pluginTypes)
      switch (t)  {
    {
      switch (t)
      {
        case PRE_OPERATION_ADD:
        case PRE_OPERATION_MODIFY:
        case PRE_OPERATION_MODIFY_DN:
        case POST_SYNCHRONIZATION_ADD:
        case POST_SYNCHRONIZATION_MODIFY:
        case POST_SYNCHRONIZATION_MODIFY_DN:
          // These are acceptable.
          break;
        default:
          Message message =
                  ERR_PLUGIN_UNIQUEATTR_INVALID_PLUGIN_TYPE.get(t.toString());
          throw new ConfigException(message);
      }
    //Load base DNs if any.
    for(DN baseDN : configuration.getUniqueAttributeBaseDN())
      baseDNs.add(baseDN);
    //Load attribute types if any.
    for(AttributeType attributeType : configuration.getUniqueAttributeType())
      uniqueAttributeTypes.add(attributeType);
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void finalizePlugin()
  {
    currentConfiguration.removeUniqueAttributeChangeListener(this);
    DirectoryServer.deregisterAlertGenerator(this);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final PreOperationPluginResult
               doPreOperation(PreOperationAddOperation addOperation)
  {
    UniqueAttributePluginCfg config = currentConfiguration;
    Entry entry = addOperation.getEntryToAdd();
    Set<DN> baseDNs = getBaseDNs(config, entry.getDN());
    if (baseDNs == null)
    {
      // The entry is outside the scope of this plugin.
      return PreOperationPluginResult.SUCCESS;
    }
    for (AttributeType t : config.getUniqueAttributeType())
    {
      List<Attribute> attrList = entry.getAttribute(t);
      if (attrList != null)
      {
        for (Attribute a : attrList)
        {
          for (AttributeValue v : a.getValues())
          {
            try
            {
              DN conflictDN = getConflictingEntryDN(baseDNs, entry.getDN(),
                                                    config, v);
              if (conflictDN != null)
              {
                addOperation.appendErrorMessage(
                     ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(t.getNameOrOID(),
                          v.getStringValue(), conflictDN.toString()));
                addOperation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
                return FAILED_PREOP_RESULT;
              }
            }
            catch (DirectoryException de)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, de);
              }
              Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
                               de.getResultCode().toString(),
                               de.getMessageObject());
              addOperation.setResultCode(
                   DirectoryServer.getServerErrorResultCode());
              addOperation.appendErrorMessage(m);
              return FAILED_PREOP_RESULT;
            }
          }
        }
      }
    }
    return PreOperationPluginResult.SUCCESS;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final PreOperationPluginResult
               doPreOperation(PreOperationModifyOperation modifyOperation)
  {
    UniqueAttributePluginCfg config = currentConfiguration;
    DN entryDN = modifyOperation.getEntryDN();
    Set<DN> baseDNs = getBaseDNs(config, entryDN);
    if (baseDNs == null)
    {
      // The entry is outside the scope of this plugin.
      return PreOperationPluginResult.SUCCESS;
    }
    for (Modification m : modifyOperation.getModifications())
    {
      Attribute a = m.getAttribute();
      AttributeType t = a.getAttributeType();
      if (! config.getUniqueAttributeType().contains(t))
      {
        // This modification isn't for a unique attribute.
        continue;
      }
      switch (m.getModificationType())
      {
        case ADD:
        case REPLACE:
          for (AttributeValue v : a.getValues())
          {
            try
            {
              DN conflictDN = getConflictingEntryDN(baseDNs, entryDN, config,
                                                    v);
              if (conflictDN != null)
              {
                modifyOperation.appendErrorMessage(
                     ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(t.getNameOrOID(),
                          v.getStringValue(), conflictDN.toString()));
                modifyOperation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
                return FAILED_PREOP_RESULT;
              }
            }
            catch (DirectoryException de)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, de);
              }
              Message message = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
                                     de.getResultCode().toString(),
                                     de.getMessageObject());
              modifyOperation.setResultCode(
                   DirectoryServer.getServerErrorResultCode());
              modifyOperation.appendErrorMessage(message);
              return FAILED_PREOP_RESULT;
            }
          }
          break;
        case INCREMENT:
          // We could calculate the new value, but we'll just take it from the
          // updated entry.
          List<Attribute> attrList =
               modifyOperation.getModifiedEntry().getAttribute(t,
                                                               a.getOptions());
          if (attrList != null)
          {
            for (Attribute updatedAttr : attrList)
            {
              if (! updatedAttr.optionsEqual(a.getOptions()))
              {
                continue;
              }
              for (AttributeValue v : updatedAttr.getValues())
              {
                try
                {
                  DN conflictDN = getConflictingEntryDN(baseDNs, entryDN,
                                                        config, v);
                  if (conflictDN != null)
                  {
                    modifyOperation.appendErrorMessage(
                         ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(
                              t.getNameOrOID(), v.getStringValue(),
                              conflictDN.toString()));
                    modifyOperation.setResultCode(
                         ResultCode.CONSTRAINT_VIOLATION);
                    return FAILED_PREOP_RESULT;
                  }
                }
                catch (DirectoryException de)
                {
                  if (debugEnabled())
                  {
                    TRACER.debugCaught(DebugLogLevel.ERROR, de);
                  }
                  Message message = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
                                         de.getResultCode().toString(),
                                         de.getMessageObject());
                  modifyOperation.setResultCode(
                       DirectoryServer.getServerErrorResultCode());
                  modifyOperation.appendErrorMessage(message);
                  return FAILED_PREOP_RESULT;
                }
              }
            }
          }
          break;
        default:
          // We don't need to look at this modification because it's not a
          // modification type of interest.
          continue;
      }
    }
    return PreOperationPluginResult.SUCCESS;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final PreOperationPluginResult doPreOperation(
                    PreOperationModifyDNOperation modifyDNOperation)
  {
    UniqueAttributePluginCfg config = currentConfiguration;
    Set<DN> baseDNs = getBaseDNs(config,
                                 modifyDNOperation.getUpdatedEntry().getDN());
    if (baseDNs == null)
    {
      // The entry is outside the scope of this plugin.
      return PreOperationPluginResult.SUCCESS;
    }
    RDN newRDN = modifyDNOperation.getNewRDN();
    for (int i=0; i < newRDN.getNumValues(); i++)
    {
      AttributeType t = newRDN.getAttributeType(i);
      if (! config.getUniqueAttributeType().contains(t))
      {
        // We aren't interested in this attribute type.
        continue;
      }
      try
      {
        AttributeValue v = newRDN.getAttributeValue(i);
        DN conflictDN = getConflictingEntryDN(baseDNs,
                             modifyDNOperation.getEntryDN(), config, v);
        if (conflictDN != null)
        {
          modifyDNOperation.appendErrorMessage(
               ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(t.getNameOrOID(),
                    v.getStringValue(), conflictDN.toString()));
          modifyDNOperation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
          return FAILED_PREOP_RESULT;
        }
      }
      catch (DirectoryException de)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, de);
        }
        Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
                         de.getResultCode().toString(),
                         de.getMessageObject());
        modifyDNOperation.setResultCode(
             DirectoryServer.getServerErrorResultCode());
        modifyDNOperation.appendErrorMessage(m);
        return FAILED_PREOP_RESULT;
      }
    }
    return PreOperationPluginResult.SUCCESS;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void doPostSynchronization(
                         PostSynchronizationAddOperation addOperation)
  {
    UniqueAttributePluginCfg config = currentConfiguration;
    Entry entry = addOperation.getEntryToAdd();
    Set<DN> baseDNs = getBaseDNs(config, entry.getDN());
    if (baseDNs == null)
    {
      // The entry is outside the scope of this plugin.
      return;
    }
    for (AttributeType t : config.getUniqueAttributeType())
    {
      List<Attribute> attrList = entry.getAttribute(t);
      if (attrList != null)
      {
        for (Attribute a : attrList)
        {
          for (AttributeValue v : a.getValues())
          {
            try
            {
              DN conflictDN = getConflictingEntryDN(baseDNs, entry.getDN(),
                                                    config, v);
              if (conflictDN != null)
              {
                Message m = ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
                                 t.getNameOrOID(),
                                 addOperation.getConnectionID(),
                                 addOperation.getOperationID(),
                                 v.getStringValue(),
                                 entry.getDN().toString(),
                                 conflictDN.toString());
                DirectoryServer.sendAlertNotification(this,
                                     ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT, m);
              }
            }
            catch (DirectoryException de)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, de);
              }
              Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
                               addOperation.getConnectionID(),
                               addOperation.getOperationID(),
                               entry.getDN().toString(),
                               de.getResultCode().toString(),
                               de.getMessageObject());
              DirectoryServer.sendAlertNotification(this,
                                   ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR, m);
            }
          }
        }
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void doPostSynchronization(
                         PostSynchronizationModifyOperation modifyOperation)
  {
    UniqueAttributePluginCfg config = currentConfiguration;
    DN entryDN = modifyOperation.getEntryDN();
    Set<DN> baseDNs = getBaseDNs(config, entryDN);
    if (baseDNs == null)
    {
      // The entry is outside the scope of this plugin.
      return;
    }
    for (Modification m : modifyOperation.getModifications())
    {
      Attribute a = m.getAttribute();
      AttributeType t = a.getAttributeType();
      if (! config.getUniqueAttributeType().contains(t))
      {
        // This modification isn't for a unique attribute.
        continue;
      }
      switch (m.getModificationType())
      {
        case ADD:
        case REPLACE:
          for (AttributeValue v : a.getValues())
          {
            try
            {
              DN conflictDN = getConflictingEntryDN(baseDNs, entryDN, config,
                                                    v);
              if (conflictDN != null)
              {
                Message message = ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
                                       t.getNameOrOID(),
                                       modifyOperation.getConnectionID(),
                                       modifyOperation.getOperationID(),
                                       v.getStringValue(),
                                       entryDN.toString(),
                                       conflictDN.toString());
                DirectoryServer.sendAlertNotification(this,
                                     ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT,
                                     message);
              }
            }
            catch (DirectoryException de)
            {
              if (debugEnabled())
              {
                TRACER.debugCaught(DebugLogLevel.ERROR, de);
              }
              Message message = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
                                    modifyOperation.getConnectionID(),
                                    modifyOperation.getOperationID(),
                                    entryDN.toString(),
                                    de.getResultCode().toString(),
                                    de.getMessageObject());
              DirectoryServer.sendAlertNotification(this,
                                   ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR, message);
            }
          }
          break;
        case INCREMENT:
          // We could calculate the new value, but we'll just take it from the
          // updated entry.
          List<Attribute> attrList =
               modifyOperation.getModifiedEntry().getAttribute(t,
                                                               a.getOptions());
          if (attrList != null)
          {
            for (Attribute updatedAttr : attrList)
            {
              if (! updatedAttr.optionsEqual(a.getOptions()))
              {
                continue;
              }
              for (AttributeValue v : updatedAttr.getValues())
              {
                try
                {
                  DN conflictDN = getConflictingEntryDN(baseDNs, entryDN,
                                                        config, v);
                  if (conflictDN != null)
                  {
                    Message message = ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
                                           t.getNameOrOID(),
                                           modifyOperation.getConnectionID(),
                                           modifyOperation.getOperationID(),
                                           v.getStringValue(),
                                           entryDN.toString(),
                                           conflictDN.toString());
                    DirectoryServer.sendAlertNotification(this,
                                         ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT,
                                         message);
                  }
                }
                catch (DirectoryException de)
                {
                  if (debugEnabled())
                  {
                    TRACER.debugCaught(DebugLogLevel.ERROR, de);
                  }
                  Message message =
                       ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
                            modifyOperation.getConnectionID(),
                            modifyOperation.getOperationID(),
                            entryDN.toString(),
                            de.getResultCode().toString(),
                            de.getMessageObject());
                  DirectoryServer.sendAlertNotification(this,
                                       ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR,
                                       message);
                }
              }
            }
          }
          break;
        default:
          // We don't need to look at this modification because it's not a
          // modification type of interest.
          continue;
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void doPostSynchronization(
                         PostSynchronizationModifyDNOperation modifyDNOperation)
  {
    UniqueAttributePluginCfg config = currentConfiguration;
    Set<DN> baseDNs = getBaseDNs(config,
                                 modifyDNOperation.getUpdatedEntry().getDN());
    if (baseDNs == null)
    {
      // The entry is outside the scope of this plugin.
      return;
    }
    RDN newRDN = modifyDNOperation.getNewRDN();
    for (int i=0; i < newRDN.getNumValues(); i++)
    {
      AttributeType t = newRDN.getAttributeType(i);
      if (! config.getUniqueAttributeType().contains(t))
      {
        // We aren't interested in this attribute type.
        continue;
      }
      try
      {
        AttributeValue v = newRDN.getAttributeValue(i);
        DN conflictDN = getConflictingEntryDN(baseDNs,
                             modifyDNOperation.getEntryDN(), config, v);
        if (conflictDN != null)
        {
          Message m =
               ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
                    t.getNameOrOID(),
                    modifyDNOperation.getConnectionID(),
                    modifyDNOperation.getOperationID(),
                    v.getStringValue(),
                    modifyDNOperation.getUpdatedEntry().getDN().toString(),
                    conflictDN.toString());
          DirectoryServer.sendAlertNotification(this,
                               ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT, m);
        }
      }
      catch (DirectoryException de)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, de);
        }
        Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
                         modifyDNOperation.getConnectionID(),
                         modifyDNOperation.getOperationID(),
                         modifyDNOperation.getUpdatedEntry().getDN().toString(),
                         de.getResultCode().toString(),
                         de.getMessageObject());
        DirectoryServer.sendAlertNotification(this,
                             ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR, m);
      }
    }
  }
  /**
   * Retrieves the set of base DNs below which uniqueness checks should be
   * performed.  If no uniqueness checks should be performed for the specified
   * entry, then {@code null} will be returned.
   *
   * @param  config   The plugin configuration to use to make the determination.
   * @param  entryDN  The DN of the entry for which the checks will be
   *                  performed.
   */
  private Set<DN> getBaseDNs(UniqueAttributePluginCfg config, DN entryDN)
  {
    Set<DN> baseDNs = config.getUniqueAttributeBaseDN();
    if ((baseDNs == null) || baseDNs.isEmpty())
    {
      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
    }
    for (DN baseDN : baseDNs)
    {
      if (entryDN.isDescendantOf(baseDN))
      {
        return baseDNs;
      }
    }
    return null;
  }
  /**
   * Retrieves the DN of the first entry identified that conflicts with the
   * provided value.
   *
   * @param  baseDNs   The set of base DNs below which the search is to be
   *                   performed.
   * @param  targetDN  The DN of the entry at which the change is targeted.  If
   *                   a conflict is found in that entry, then it will be
   *                   ignored.
   * @param  config    The plugin configuration to use when making the
   *                   determination.
   * @param  value     The value for which to identify any conflicting entries.
   *
   * @return  The DN of the first entry identified that contains a conflicting
   *          value.
   *
   * @throws  DirectoryException  If a problem occurred while attempting to
   *                              make the determination.
   */
  private DN getConflictingEntryDN(Set<DN> baseDNs, DN targetDN,
                                   UniqueAttributePluginCfg config,
                                   AttributeValue value)
          throws DirectoryException
  {
    SearchFilter filter;
    Set<AttributeType> attrTypes = config.getUniqueAttributeType();
    if (attrTypes.size() == 1)
    {
      filter = SearchFilter.createEqualityFilter(attrTypes.iterator().next(),
                                                 value);
    }
    else
    {
      ArrayList<SearchFilter> equalityFilters =
           new ArrayList<SearchFilter>(attrTypes.size());
      for (AttributeType t : attrTypes)
      {
        equalityFilters.add(SearchFilter.createEqualityFilter(t, value));
      }
      filter = SearchFilter.createORFilter(equalityFilters);
    }
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    for (DN baseDN : baseDNs)
    {
      InternalSearchOperation searchOperation =
           conn.processSearch(baseDN, SearchScope.WHOLE_SUBTREE,
                              DereferencePolicy.NEVER_DEREF_ALIASES, 2, 0,
                              false, filter, SEARCH_ATTRS);
      for (SearchResultEntry e : searchOperation.getSearchEntries())
      {
        if (! e.getDN().equals(targetDN))
        {
          return e.getDN();
        }
      }
      switch (searchOperation.getResultCode())
      {
        case SUCCESS:
        case NO_SUCH_OBJECT:
          // These are fine.  Either the search was successful or the base DN
          // didn't exist.
          break;
        default:
          // An error occurred that prevented the search from completing
          // successfully.
          throw new DirectoryException(searchOperation.getResultCode(),
                         searchOperation.getErrorMessage().toMessage());
      }
    }
    // If we've gotten here, then no conflict was found.
    return null;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean isConfigurationAcceptable(PluginCfg configuration,
                                           List<Message> unacceptableReasons)
  {
    UniqueAttributePluginCfg cfg = (UniqueAttributePluginCfg) configuration;
    return isConfigurationChangeAcceptable(cfg, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
          UniqueAttributePluginCfg configuration,
          List<Message> unacceptableReasons) {
                      UniqueAttributePluginCfg configuration,
                      List<Message> unacceptableReasons)
  {
    boolean configAcceptable = true;
    for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType())
    {
@@ -113,12 +843,15 @@
        case PREOPERATIONADD:
        case PREOPERATIONMODIFY:
        case PREOPERATIONMODIFYDN:
        case POSTSYNCHRONIZATIONADD:
        case POSTSYNCHRONIZATIONMODIFY:
        case POSTSYNCHRONIZATIONMODIFYDN:
          // These are acceptable.
          break;
        default:
          Message message =
           ERR_PLUGIN_UNIQUEATTR_INVALID_PLUGIN_TYPE.get(pluginType.toString());
          Message message = ERR_PLUGIN_UNIQUEATTR_INVALID_PLUGIN_TYPE.get(
                                 pluginType.toString());
          unacceptableReasons.add(message);
          configAcceptable = false;
      }
@@ -126,337 +859,53 @@
    return configAcceptable;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
          UniqueAttributePluginCfg newConfiguration) {
    ResultCode resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<Message> messages            = new ArrayList<Message>();
    LinkedHashSet<AttributeType> newUniqueattributeTypes=
                                             new LinkedHashSet<AttributeType>();
    LinkedHashSet<DN> newConfiguredBaseDNs = new LinkedHashSet<DN>();
    //Load base DNs from new configuration.
    for(DN baseDN : newConfiguration.getUniqueAttributeBaseDN())
      newConfiguredBaseDNs.add(baseDN);
    //Load attribute types from new configuration.
    for(AttributeType attributeType : newConfiguration.getUniqueAttributeType())
      newUniqueattributeTypes.add(attributeType);
    //Switch to the new lists and configurations.
    baseDNs = newConfiguredBaseDNs;
    uniqueAttributeTypes = newUniqueattributeTypes;
                                 UniqueAttributePluginCfg newConfiguration)
  {
    currentConfiguration = newConfiguration;
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final PreOperationPluginResult
               doPreOperation(PreOperationAddOperation addOperation) {
    PreOperationPluginResult pluginResult=PreOperationPluginResult.SUCCESS;
    DN entryDN=addOperation.getEntryDN();
    if(isEntryUniquenessCandidate(entryDN)) {
      List<AttributeValue> valueList =
                         getEntryAttributeValues(addOperation.getEntryToAdd());
      if(!searchAllBaseDNs(valueList, entryDN))
        pluginResult =  getPluginErrorResult(addOperation,
                ERR_PLUGIN_UNIQUEATTR_ADD_NOT_UNIQUE.get(entryDN.toString()));
    }
    return pluginResult;
  public DN getComponentEntryDN()
  {
    return currentConfiguration.dn();
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final PreOperationPluginResult
  doPreOperation(PreOperationModifyOperation modifyOperation) {
    PreOperationPluginResult pluginResult=PreOperationPluginResult.SUCCESS;
    DN entryDN = modifyOperation.getEntryDN();
    if(isEntryUniquenessCandidate(entryDN)) {
      List<AttributeValue> valueList =
              getModificationAttributeValues(modifyOperation.getModifications(),
                                        modifyOperation.getModifiedEntry());
      if(!searchAllBaseDNs(valueList, entryDN))
        pluginResult =  getPluginErrorResult(modifyOperation,
                  ERR_PLUGIN_UNIQUEATTR_MOD_NOT_UNIQUE.get(entryDN.toString()));
    }
    return pluginResult;
  public String getClassName()
  {
    return UniqueAttributePlugin.class.getName();
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final PreOperationPluginResult
               doPreOperation(PreOperationModifyDNOperation modifyDNOperation) {
    PreOperationPluginResult pluginResult=PreOperationPluginResult.SUCCESS;
    DN entryDN=modifyDNOperation.getOriginalEntry().getDN();
    //If the operation has a new superior DN then use that, since any moves
    //need to make sure there are no conflicts in the new superior base DN.
    if(modifyDNOperation.getNewSuperior() != null)
        entryDN = modifyDNOperation.getNewSuperior();
    if(isEntryUniquenessCandidate(entryDN)) {
      List<AttributeValue> valueList =
              getRDNAttributeValues(modifyDNOperation.getNewRDN());
      if(!searchAllBaseDNs(valueList, entryDN))
        pluginResult =  getPluginErrorResult(modifyDNOperation,
                ERR_PLUGIN_UNIQUEATTR_MODDN_NOT_UNIQUE.get(entryDN.toString()));
    }
    return pluginResult;
  }
  public LinkedHashMap<String,String> getAlerts()
  {
    LinkedHashMap<String,String> alerts = new LinkedHashMap<String,String>(2);
  /**
   * Determine if the specified DN is a candidate for attribute uniqueness
   * checking. Checking is skipped if the the unique attribute type list is
   * empty or if there are base DNS configured and the specified DN is not a
   * descendant of any of them. Checking is performed for all other cases.
   *
   * @param dn The DN to check.
   *
   * @return Returns <code>true</code> if the operation needs uniqueness
   *         checking performed.
   */
  private boolean
  isEntryUniquenessCandidate(DN dn) {
    if(uniqueAttributeTypes.isEmpty())
      return false;
    else if(baseDNs.isEmpty())
      return true;
    else {
      for(DN baseDN : baseDNs)
        if(baseDN.isAncestorOf(dn))
          return true;
    }
    return false;
  }
    alerts.put(ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT,
               ALERT_DESCRIPTION_UNIQUE_ATTR_SYNC_CONFLICT);
    alerts.put(ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR,
               ALERT_DESCRIPTION_UNIQUE_ATTR_SYNC_ERROR);
  /**
   * Returns a plugin result instance indicating that the operation should be
   * terminated; that no further pre-operation processing should be performed
   * and that the server should send the response immediately. It also adds
   * a CONSTRAINT_VIOLATION result code and the specified error message to
   * the specified operation.
   *
   * @param operation   The operation to add the result code and message to.
   *
   * @param message The message to add to the operation.
   *
   * @return Returns a plugin result instance that halts further processing
   *         on this operation.
   */
  private PreOperationPluginResult
  getPluginErrorResult(PreOperationOperation operation, Message message) {
        operation.appendErrorMessage(message);
        operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
        return new PreOperationPluginResult(false, false, true);
  }
  /**
   * Searches all of the the attribute types of the specified RDN for matches
   * in the unique attribute type list. If matches are found, then the
   * corresponding values are added to a list of values that will be eventually
   * searched for uniqueness.
   * @param rdn  The RDN to examine.
   *
   * @return Returns a list of attribute values from the RDN that matches the
   *         unique attribute type list.
   */
  private List<AttributeValue> getRDNAttributeValues(RDN rdn) {
    LinkedList<AttributeValue> valueList=
                                            new LinkedList<AttributeValue>();
    int numAVAs = rdn.getNumValues();
    for (int i = 0; i < numAVAs; i++) {
      if(uniqueAttributeTypes.contains(rdn.getAttributeType(i)))
        valueList.add(rdn.getAttributeValue(i));
    }
    return valueList;
  }
  /**
   * Searches all of the attribute types of the specified entry for matches
   * in the unique attribute type list. Ff matches are found, then the
   * corresponding values are added to a list of values that will eventually
   * be searched for uniqueness.
   *
   * @param entry The entry to examine.
   *
   * @return Returns a list of attribute values from the entry that matches the
   *         unique attribute type list.
   */
  private List<AttributeValue> getEntryAttributeValues(Entry entry) {
    LinkedList<AttributeValue> valueList=new LinkedList<AttributeValue>();
    for(AttributeType attributeType : uniqueAttributeTypes) {
      if(entry.hasAttribute(attributeType))  {
        List<Attribute> attrList=entry.getAttribute(attributeType);
        for (Attribute a : attrList)
          valueList.addAll(a.getValues());
      }
    }
    return valueList;
  }
  /**
   * Iterate over the unique attribute type list calling a method that will
   * search the specified modification list for each attribute type and add
   * the corresponding values to a list of values.
   *
   * @param modificationList  The modification list to search over.
   *
   * @param modifedEntry The copy of the entry with modifications applied.
   *
   * @return Returns a list of attribute values from the modification list
   *         that matches the unique attribute type list.
   */
  private List<AttributeValue>
  getModificationAttributeValues(List<Modification>  modificationList,
                            Entry modifedEntry)  {
    LinkedList<AttributeValue> valueList =
                                            new LinkedList<AttributeValue>();
    for(AttributeType attributeType : uniqueAttributeTypes)
      getModValuesForAttribute(modificationList, attributeType, valueList,
                               modifedEntry);
    return valueList;
  }
  /**
   * Searches the specified modification list for the provided attribute type.
   * If a match is found than the attribute value is added to a list of
   * attribute values that will be eventually searched for uniqueness.
   *
   * @param modificationList The modification list to search over.
   *
   * @param attributeType The attribute type to search for.
   *
   * @param valueList A list of attribute values to put the values in.
   *
   * @param modifiedEntry A copy of the entry with modifications applied.
   */
  private void
  getModValuesForAttribute(List<Modification> modificationList,
                           AttributeType attributeType,
                           LinkedList<AttributeValue> valueList,
                           Entry modifiedEntry) {
    for(Modification modification : modificationList) {
      ModificationType modType=modification.getModificationType();
      //Skip delete modifications or modifications on attribute types not
      //matching the specified type.
      if(modType == ModificationType.DELETE ||
         !modification.getAttribute().getAttributeType().equals(attributeType))
          continue;
      //Increment uses modified entry to get value for the attribute type.
      if(modType == ModificationType.INCREMENT) {
        List<Attribute> modifiedAttrs =
           modifiedEntry.getAttribute(attributeType,
                                      modification.getAttribute().getOptions());
        if (modifiedAttrs != null)  {
          for (Attribute a : modifiedAttrs)
            valueList.addAll(a.getValues());
        }
      } else {
        Attribute modifiedAttribute=modification.getAttribute();
        if(modifiedAttribute.hasValue())
          valueList.addAll(modifiedAttribute.getValues());
      }
    }
  }
  /**
   * Iterates over the base DNs configured by the plugin entry searching for
   * value matches. If the base DN list is empty then the public naming
   * contexts are used instead.
   *
   * @param valueList The list of values to search for.
   *
   * @param entryDN  The DN of the entry related to the operation.
   *
   * @return  Returns <code>true</code> if a value is unique.
   */
  private boolean
  searchAllBaseDNs(List<AttributeValue> valueList, DN entryDN) {
    if(valueList.isEmpty())
      return true;
    if(baseDNs.isEmpty()) {
      for(DN baseDN : DirectoryServer.getPublicNamingContexts().keySet()) {
        if(searchBaseDN(valueList, baseDN, entryDN))
          return false;
      }
    } else {
      for(DN baseDN : baseDNs)  {
        if(searchBaseDN(valueList, baseDN, entryDN))
          return false;
      }
    }
    return true;
  }
  /**
   * Search a single base DN for all the values in a specified value list.
   * A filter is created to search all the attribute at once for each
   * value in the list.
   *
   * @param valueList The list of values to search for.
   *
   * @param baseDN  The base DN to base the search at.
   *
   * @param entryDN  The DN of the entry related to the operation.
   *
   * @return Returns <code>true</code> if the values are not unique under the
   *         under the base DN.
   */
  private boolean
  searchBaseDN(List<AttributeValue> valueList, DN baseDN,
                    DN entryDN) {
    //Filter set to hold component filters.
    HashSet<SearchFilter> componentFilters=new HashSet<SearchFilter>();
    for(AttributeValue value : valueList) {
      //Iterate over the unique attribute list and build a equality filter
      //using each attribute type in the list and the current value being
      //matched.
      for(AttributeType attributeType : uniqueAttributeTypes)
        componentFilters.add(SearchFilter.createEqualityFilter(attributeType,
                value));
      //Perform the search using the OR filter created from the filter
      //components created above.
      InternalClientConnection conn =
              InternalClientConnection.getRootConnection();
      InternalSearchOperation operation = conn.processSearch(baseDN,
              SearchScope.WHOLE_SUBTREE,
              DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, true,
              SearchFilter.createORFilter(componentFilters),
              null);
      switch (operation.getResultCode()) {
        case SUCCESS:
          break;
        case NO_SUCH_OBJECT:
          //This base DN doesn't exist, return false.
          return false;
        case SIZE_LIMIT_EXCEEDED:
        case TIME_LIMIT_EXCEEDED:
        case ADMIN_LIMIT_EXCEEDED:
        default:
          //Couldn't determine if the attribute is unique because an
          //administrative limit was reached during the search. Fail the
          //operation by returning true. Possibly log an error here?
          return true;
      }
      for (SearchResultEntry entry : operation.getSearchEntries()) {
        //Only allow the entry DN to exist. The user might be modifying
        //the attribute values and putting the same value back. Any other entry
        //means the value is not unique.
        if(!entry.getDN().equals(entryDN))
          return true;
      }
      componentFilters.clear();
    }
    return false;
    return alerts;
  }
}
opends/src/server/org/opends/server/types/operation/PostSynchronizationAddOperation.java
New file
@@ -0,0 +1,136 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.types.operation;
import java.util.List;
import java.util.Map;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.ByteString;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.RawAttribute;
/**
 * This class defines a set of methods that are available for use by
 * post-synchronization plugins for add operations.  Note that this
 * interface is intended only to define an API for use by plugins and
 * is not intended to be implemented by any custom classes.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
     mayInstantiate=false,
     mayExtend=false,
     mayInvoke=true)
public interface PostSynchronizationAddOperation
       extends PostSynchronizationOperation
{
  /**
   * Retrieves the DN of the entry to add in a raw, unparsed form as
   * it was included in the request.  This may or may not actually
   * contain a valid DN, since no validation will have been performed
   * on it.
   *
   * @return  The DN of the entry in a raw, unparsed form.
   */
  public ByteString getRawEntryDN();
  /**
   * Retrieves the set of attributes in their raw, unparsed form as
   * read from the client request.  Some of these attributes may be
   * invalid as no validation will have been performed on them.  The
   * returned list must not be altered by the caller.
   *
   * @return  The set of attributes in their raw, unparsed form as
   *          read from the client request.
   */
  public List<RawAttribute> getRawAttributes();
  /**
   * Retrieves the DN of the entry to add.
   *
   * @return  The DN of the entry to add.
   */
  public DN getEntryDN();
  /**
   * Retrieves the set of processed objectclasses for the entry to
   * add.  The contents of the returned map must not be altered by the
   * caller.
   *
   * @return  The set of processed objectclasses for the entry to add.
   */
  public Map<ObjectClass,String> getObjectClasses();
  /**
   * Retrieves the set of processed user attributes for the entry to
   * add.  The contents of the returned map must not be altered by the
   * caller.
   *
   * @return  The set of processed user attributes for the entry to
   *          add.
   */
  public Map<AttributeType,List<Attribute>> getUserAttributes();
  /**
   * Retrieves the set of processed operational attributes for the
   * entry to add.  The contents of the returned map must not be
   * altered by the caller.
   *
   * @return  The set of processed operational attributes for the
   *          entry to add.
   */
  public Map<AttributeType,List<Attribute>>
              getOperationalAttributes();
  /**
   * Retrieves the entry to be added to the server.  The contents of
   * the returned entry must not be altered by the caller.
   *
   * @return  The entry to be added to the server.
   */
  public Entry getEntryToAdd();
}
opends/src/server/org/opends/server/types/operation/PostSynchronizationDeleteOperation.java
New file
@@ -0,0 +1,78 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.types.operation;
import org.opends.server.types.ByteString;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
/**
 * This class defines a set of methods that are available for use by
 * post-synchronization plugins for delete operations.  Note that this
 * interface is intended only to define an API for use by plugins and
 * is not intended to be implemented by any custom classes.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
     mayInstantiate=false,
     mayExtend=false,
     mayInvoke=true)
public interface PostSynchronizationDeleteOperation
       extends PostSynchronizationOperation
{
  /**
   * Retrieves the raw, unprocessed entry DN as included in the client
   * request.
   *
   * @return  The raw, unprocessed entry DN as included in the client
   *          request.
   */
  public ByteString getRawEntryDN();
  /**
   * Retrieves the DN of the entry to delete.
   *
   * @return  The DN of the entry to delete.
   */
  public DN getEntryDN();
  /**
   * Retrieves the entry to be deleted.
   *
   * @return  The entry to be deleted.
   */
  public Entry getEntryToDelete();
}
opends/src/server/org/opends/server/types/operation/PostSynchronizationModifyDNOperation.java
New file
@@ -0,0 +1,189 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.types.operation;
import java.util.List;
import org.opends.server.types.ByteString;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
import org.opends.server.types.RDN;
/**
 * This class defines a set of methods that are available for use by
 * post-synchronization plugins for modify DN operations.  Note that
 * this interface is intended only to define an API for use by plugins
 * and is not intended to be implemented by any custom classes.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
     mayInstantiate=false,
     mayExtend=false,
     mayInvoke=true)
public interface PostSynchronizationModifyDNOperation
       extends PostSynchronizationOperation
{
  /**
   * Retrieves the raw, unprocessed entry DN as included in the client
   * request.  The DN that is returned may or may not be a valid DN,
   * since no validation will have been performed upon it.
   *
   * @return  The raw, unprocessed entry DN as included in the client
   *          request.
   */
  public ByteString getRawEntryDN();
  /**
   * Retrieves the DN of the entry to rename.  This should not be
   * called by pre-parse plugins because the processed DN will not be
   * available yet.  Instead, they should call the
   * <CODE>getRawEntryDN</CODE> method.
   *
   * @return  The DN of the entry to rename, or <CODE>null</CODE> if
   *          the raw entry DN has not yet been processed.
   */
  public DN getEntryDN();
  /**
   * Retrieves the raw, unprocessed newRDN as included in the request
   * from the client.  This may or may not contain a valid RDN, as no
   * validation will have been performed on it.
   *
   * @return  The raw, unprocessed newRDN as included in the request
   *          from the client.
   */
  public ByteString getRawNewRDN();
  /**
   * Retrieves the new RDN to use for the entry.  This should not be
   * called by pre-parse plugins, because the processed newRDN will
   * not yet be available.  Pre-parse plugins should instead use the
   * <CODE>getRawNewRDN</CODE> method.
   *
   * @return  The new RDN to use for the entry, or <CODE>null</CODE>
   *          if the raw newRDN has not yet been processed.
   */
  public RDN getNewRDN();
  /**
   * Indicates whether the current RDN value should be removed from
   * the entry.
   *
   * @return  <CODE>true</CODE> if the current RDN value should be
   *          removed from the entry, or <CODE>false</CODE> if not.
   */
  public boolean deleteOldRDN();
  /**
   * Retrieves the raw, unprocessed newSuperior from the client
   * request.  This may or may not contain a valid DN, as no
   * validation will have been performed on it.
   *
   * @return  The raw, unprocessed newSuperior from the client
   *          request, or <CODE>null</CODE> if there is none.
   */
  public ByteString getRawNewSuperior();
  /**
   * Retrieves the newSuperior DN for the entry.  This should not be
   * called by pre-parse plugins, because the processed DN will not
   * yet be available at that time.  Instead, they should use the
   * <CODE>getRawNewSuperior</CODE> method.
   *
   * @return  The newSuperior DN for the entry, or <CODE>null</CODE>
   *          if there is no newSuperior DN for this request or if the
   *          raw newSuperior has not yet been processed.
   */
  public DN getNewSuperior();
  /**
   * Retrieves the set of modifications applied to attributes of the
   * target entry in the course of processing this modify DN
   * operation.  This will include attribute-level changes from the
   * modify DN itself (e.g., removing old RDN values if deleteOldRDN
   * is set, or adding new RDN values that don't already exist), but
   * it may also be used by pre-operation plugins to cause additional
   * changes in the entry.  In this case, those plugins may add
   * modifications to this list through the
   * <CODE>addModification</CODE> method (the list returned from this
   * method should not be modified directly) if any changes should be
   * processed in addition to the core modify DN processing.  Backends
   * may read this list to identify which attribute-level changes were
   * applied in order to more easily apply updates to attribute
   * indexes.
   *
   * @return  The set of modifications applied to attributes during
   *          the course of the modify DN processing, or
   *          <CODE>null</CODE> if that information is not yet
   *          available (e.g., during pre-parse plugins).
   */
  public List<Modification> getModifications();
  /**
   * Retrieves the current entry, before it is renamed.  This will not
   * be available to pre-parse plugins or during the conflict
   * resolution portion of the synchronization processing.
   *
   * @return  The current entry, or <CODE>null</CODE> if it is not yet
   *           available.
   */
  public Entry getOriginalEntry();
  /**
   * Retrieves the new entry, as it will appear after it is renamed.
   * This will not be  available to pre-parse plugins or during the
   * conflict resolution portion of the synchronization processing.
   *
   * @return  The updated entry, or <CODE>null</CODE> if it is not yet
   *           available.
   */
  public Entry getUpdatedEntry();
}
opends/src/server/org/opends/server/types/operation/PostSynchronizationModifyOperation.java
New file
@@ -0,0 +1,148 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.types.operation;
import java.util.List;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
import org.opends.server.types.RawModification;
/**
 * This class defines a set of methods that are available for use by
 * post-synchronization plugins for modify operations.  Note that this
 * interface is intended only to define an API for use by plugins and
 * is not intended to be implemented by any custom classes.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
     mayInstantiate=false,
     mayExtend=false,
     mayInvoke=true)
public interface PostSynchronizationModifyOperation
       extends PostSynchronizationOperation
{
  /**
   * Retrieves the raw, unprocessed entry DN as included in the client
   * request.  The DN that is returned may or may not be a valid DN,
   * since no validation will have been performed upon it.
   *
   * @return  The raw, unprocessed entry DN as included in the client
   *          request.
   */
  public ByteString getRawEntryDN();
  /**
   * Retrieves the DN of the entry to modify.
   *
   * @return  The DN of the entry to modify.
   */
  public DN getEntryDN();
  /**
   * Retrieves the set of raw, unprocessed modifications as included
   * in the client request.  Note that this may contain one or more
   * invalid modifications, as no validation will have been performed
   * on this information.  The list returned must not be altered by
   * the caller.
   *
   * @return  The set of raw, unprocessed modifications as included
   *          in the client request.
   */
  public List<RawModification> getRawModifications();
  /**
   * Retrieves the set of modifications for this modify operation.
   Its contents should not be altered.
   *
   * @return  The set of modifications for this modify operation.
   */
  public List<Modification> getModifications();
  /**
   * Retrieves the current entry before any modifications are applied.
   * It should not be modified by the caller.
   *
   * @return  The current entry before any modifications are applied.
   */
  public Entry getCurrentEntry();
  /**
   * Retrieves the modified entry that is to be written to the
   * backend.  It should not be modified by the caller.
   *
   * @return  The modified entry that is to be written to the backend.
   */
  public Entry getModifiedEntry();
  /**
   * Retrieves the set of clear-text current passwords for the user,
   * if available.  This will only be available if the modify
   * operation contains one or more delete elements that target the
   * password attribute and provide the values to delete in the clear.
   * This list should not be altered by the caller.
   *
   * @return  The set of clear-text current password values as
   *          provided in the modify request, or <CODE>null</CODE> if
   *          there were none.
   */
  public List<AttributeValue> getCurrentPasswords();
  /**
   * Retrieves the set of clear-text new passwords for the user, if
   * available.  This will only be available if the modify operation
   * contains one or more add or replace elements that target the
   * password attribute and provide the values in the clear.  This
   * list should not be altered by the caller.
   *
   * @return  The set of clear-text new passwords as provided in the
   *          modify request, or <CODE>null</CODE> if there were none.
   */
  public List<AttributeValue> getNewPasswords();
}
opends/src/server/org/opends/server/types/operation/PostSynchronizationOperation.java
New file
@@ -0,0 +1,144 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.types.operation;
import org.opends.messages.MessageBuilder;
import java.util.List;
import org.opends.server.types.DN;
import org.opends.server.types.ResultCode;
/**
 * This class defines a set of methods that are available for use by
 * post-synchronization plugins for all types of operations.  Note
 * that this interface is intended only to define an API for use by
 * plugins and is not intended to be implemented by any custom
 * classes.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
     mayInstantiate=false,
     mayExtend=false,
     mayInvoke=true)
public interface PostSynchronizationOperation
       extends PluginOperation
{
  /**
   * Retrieves the result code for this operation.
   *
   * @return  The result code associated for this operation, or
   *          <CODE>UNDEFINED</CODE> if the operation has not yet
   *          completed.
   */
  public ResultCode getResultCode();
  /**
   * Retrieves the error message for this operation.  Its contents may
   * be altered by the caller.
   *
   * @return  The error message for this operation.
   */
  public MessageBuilder getErrorMessage();
  /**
   * Retrieves the additional log message for this operation, which
   * should be written to the log but not included in the response to
   * the client.  The contents of this buffer may be altered by the
   * caller.
   *
   * @return  The additional log message for this operation.
   */
  public MessageBuilder getAdditionalLogMessage();
  /**
   * Retrieves the matched DN for this operation.
   *
   * @return  The matched DN for this operation, or <CODE>null</CODE>
   *          if the operation has not yet completed or does not have
   *          a matched DN.
   */
  public DN getMatchedDN();
  /**
   * Retrieves the set of referral URLs for this operation.  Its
   * contents must not be altered by the caller.
   *
   * @return  The set of referral URLs for this operation, or
   *          <CODE>null</CODE> if the operation is not yet complete
   *          or does not have a set of referral URLs.
   */
  public List<String> getReferralURLs();
  /**
   * Retrieves the authorization DN for this operation.  In many
   * cases, it will be the same as the DN of the authenticated user
   * for the underlying connection, or the null DN if no
   * authentication has been performed on that connection.  However,
   * it may be some other value if special processing has been
   * requested (e.g., the operation included a proxied authorization
   * control).
   *
   * @return  The authorization DN for this operation.
   */
  public DN getAuthorizationDN();
  /**
   * Retrieves the time that processing stopped for this operation.
   * This will actually hold a time immediately before the response
   * was sent to the client.
   *
   * @return  The time that processing stopped for this operation.
   */
  public long getProcessingStopTime();
  /**
   * Retrieves the length of time in milliseconds that the server
   * spent processing this operation.
   *
   * @return  The length of time in milliseconds that the server spent
   *          processing this operation.
   */
  public long getProcessingTime();
}
opends/src/server/org/opends/server/util/ServerConstants.java
@@ -1703,6 +1703,50 @@
  /**
   * The description for the alert type that will be used for the alert
   * notification generated if a unique attribute conflict is detected during
   * synchronization processing.
   */
  public static final String ALERT_DESCRIPTION_UNIQUE_ATTR_SYNC_CONFLICT =
       "This alert type will be used to provide notification that a unique " +
       "attribute conflict has been detected during synchronization " +
       "processing.";
  /**
   * The alert type string that will be used for the alert notification
   * generated if a unique attribute conflict is detected during synchronization
   * processing.
   */
  public static final String ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT =
       "org.opends.server.UniqueAttributeSynchronizationConflict";
  /**
   * The description for the alert type that will be used for the alert
   * notification generated if an error occurs while attempting to perform
   * unique attribute conflict detection during synchronization processing.
   */
  public static final String ALERT_DESCRIPTION_UNIQUE_ATTR_SYNC_ERROR =
       "This alert type will be used to provide notification that an error " +
       "occurred while attempting to perform unique attribute conflict " +
       "detection during synchronization processing.";
  /**
   * The alert type string that will be used for the alert notification
   * generated if an error occurs while attempting to perform unique attribute
   * conflict detection during synchronization processing.
   */
  public static final String ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR =
       "org.opends.server.UniqueAttributeSynchronizationError";
  /**
   * The name of the default password storage scheme that will be used for new
   * passwords.
   */
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
@@ -63,6 +63,7 @@
import org.opends.server.types.operation.PostOperationAddOperation;
import org.opends.server.types.operation.PostResponseAddOperation;
import org.opends.server.types.operation.PreOperationAddOperation;
import org.opends.server.types.operation.PostSynchronizationAddOperation;
import org.opends.server.util.TimeThread;
/**
@@ -72,7 +73,8 @@
public class LocalBackendAddOperation extends AddOperationWrapper
  implements PreOperationAddOperation,
             PostOperationAddOperation,
             PostResponseAddOperation
             PostResponseAddOperation,
             PostSynchronizationAddOperation
{
  // The entry being added to the server.
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
@@ -33,6 +33,7 @@
import org.opends.server.types.operation.PostOperationDeleteOperation;
import org.opends.server.types.operation.PostResponseDeleteOperation;
import org.opends.server.types.operation.PreOperationDeleteOperation;
import org.opends.server.types.operation.PostSynchronizationDeleteOperation;
/**
 * This class defines an operation used to delete an entry in a local backend
@@ -41,7 +42,8 @@
public class LocalBackendDeleteOperation extends DeleteOperationWrapper
  implements PreOperationDeleteOperation,
             PostOperationDeleteOperation,
             PostResponseDeleteOperation
             PostResponseDeleteOperation,
             PostSynchronizationDeleteOperation
{
  // The entry to be deleted.
  private Entry entry;
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
@@ -32,6 +32,7 @@
import org.opends.server.types.operation.PostOperationModifyDNOperation;
import org.opends.server.types.operation.PostResponseModifyDNOperation;
import org.opends.server.types.operation.PreOperationModifyDNOperation;
import org.opends.server.types.operation.PostSynchronizationModifyDNOperation;
/**
 * This class defines an operation used to move an entry in a local backend
@@ -41,7 +42,8 @@
  extends ModifyDNOperationWrapper
  implements PreOperationModifyDNOperation,
             PostOperationModifyDNOperation,
             PostResponseModifyDNOperation
             PostResponseModifyDNOperation,
             PostSynchronizationModifyDNOperation
{
  // The current entry, before it is renamed.
  private Entry currentEntry;
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
@@ -37,6 +37,7 @@
import org.opends.server.types.operation.PostOperationModifyOperation;
import org.opends.server.types.operation.PostResponseModifyOperation;
import org.opends.server.types.operation.PreOperationModifyOperation;
import org.opends.server.types.operation.PostSynchronizationModifyOperation;
/**
 * This class defines an operation used to modify an entry in a local backend
@@ -45,7 +46,8 @@
public class LocalBackendModifyOperation extends ModifyOperationWrapper
  implements PreOperationModifyOperation,
             PostOperationModifyOperation,
             PostResponseModifyOperation
             PostResponseModifyOperation,
             PostSynchronizationModifyOperation
{
  // The current entry, before any changes are applied.
  private Entry currentEntry = null;
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -2416,8 +2416,15 @@
    // Indicate that it is now too late to attempt to cancel the operation.
    localOp.setCancelResult(CancelResult.TOO_LATE);
    // Invoke the post-operation modify plugins.
    if (! skipPostOperation)
    // Invoke the post-operation or post-synchronization modify plugins.
    if (localOp.isSynchronizationOperation())
    {
      if (localOp.getResultCode() == ResultCode.SUCCESS)
      {
        pluginConfigManager.invokePostSynchronizationModifyPlugins(localOp);
      }
    }
    else if (! skipPostOperation)
    {
      // FIXME -- Should this also be done while holding the locks?
      PostOperationPluginResult postOpResult =
@@ -5893,8 +5900,15 @@
    localOp.setCancelResult(CancelResult.TOO_LATE);
    // Invoke the post-operation add plugins.
    if (! skipPostOperation)
    // Invoke the post-operation or post-synchronization add plugins.
    if (localOp.isSynchronizationOperation())
    {
      if (localOp.getResultCode() == ResultCode.SUCCESS)
      {
        pluginConfigManager.invokePostSynchronizationAddPlugins(localOp);
      }
    }
    else if (! skipPostOperation)
    {
      // FIXME -- Should this also be done while holding the locks?
      PostOperationPluginResult postOpResult =
@@ -6672,8 +6686,15 @@
    localOp.setCancelResult(CancelResult.TOO_LATE);
    // Invoke the post-operation delete plugins.
    if (! skipPostOperation)
    // Invoke the post-operation or post-synchronization delete plugins.
    if (localOp.isSynchronizationOperation())
    {
      if (localOp.getResultCode() == ResultCode.SUCCESS)
      {
        pluginConfigManager.invokePostSynchronizationDeletePlugins(localOp);
      }
    }
    else if (! skipPostOperation)
    {
      PostOperationPluginResult postOperationResult =
           pluginConfigManager.invokePostOperationDeletePlugins(localOp);
@@ -8498,8 +8519,15 @@
    op.setCancelResult(CancelResult.TOO_LATE);
    // Invoke the post-operation modify DN plugins.
    if (! skipPostOperation)
    // Invoke the post-operation or post-synchronization modify DN plugins.
    if (op.isSynchronizationOperation())
    {
      if (op.getResultCode() == ResultCode.SUCCESS)
      {
        pluginConfigManager.invokePostSynchronizationModifyDNPlugins(op);
      }
    }
    else if (! skipPostOperation)
    {
      PostOperationPluginResult postOperationResult =
           pluginConfigManager.invokePostOperationModifyDNPlugins(op);
opends/tests/unit-tests-testng/resource/config-changes.ldif
@@ -48,6 +48,14 @@
replace: ds-cfg-suppress-internal-operations
ds-cfg-suppress-internal-operations: false
dn: cn=File-Based Audit Logger,cn=Loggers,cn=config
changetype: modify
replace: ds-cfg-logger-enabled
ds-cfg-logger-enabled: true
-
replace: ds-cfg-suppress-internal-operations
ds-cfg-suppress-internal-operations: false
dn: cn=Test Password Validator,cn=Password Validators,cn=config
changetype: add
objectClass: top
@@ -166,6 +174,7 @@
ds-cfg-plugin-type: preOperationModify
ds-cfg-plugin-type: preOperationModifyDN
ds-cfg-plugin-type: preOperationSearch
ds-cfg-invoke-for-internal-operations: true
dn: cn=Disconnect Client Plugin,cn=Plugins,cn=config
changetype: add
@@ -210,6 +219,7 @@
ds-cfg-plugin-type: postResponseModify
ds-cfg-plugin-type: postResponseModifyDN
ds-cfg-plugin-type: postResponseSearch
ds-cfg-invoke-for-internal-operations: true
dn: cn=Invocation Counter Plugin,cn=Plugins,cn=config
changetype: add
@@ -254,6 +264,9 @@
ds-cfg-plugin-type: postResponseModify
ds-cfg-plugin-type: postResponseModifyDN
ds-cfg-plugin-type: postResponseSearch
ds-cfg-plugin-type: postSynchronizationAdd
ds-cfg-plugin-type: postSynchronizationModify
ds-cfg-plugin-type: postSynchronizationModifyDN
ds-cfg-plugin-type: searchResultEntry
ds-cfg-plugin-type: searchResultReference
ds-cfg-plugin-type: subordinateModifyDN
@@ -264,6 +277,7 @@
ds-cfg-plugin-type: ldifExport
ds-cfg-plugin-type: startup
ds-cfg-plugin-type: shutdown
ds-cfg-invoke-for-internal-operations: true
dn: cn=Short Circuit Plugin,cn=Plugins,cn=config
changetype: add
@@ -290,6 +304,7 @@
ds-cfg-plugin-type: preOperationModify
ds-cfg-plugin-type: preOperationModifyDN
ds-cfg-plugin-type: preOperationSearch
ds-cfg-invoke-for-internal-operations: true
dn: cn=Update PreOperation Plugin,cn=Plugins,cn=config
changetype: add
@@ -300,6 +315,7 @@
ds-cfg-plugin-enabled: true
ds-cfg-plugin-type: preOperationAdd
ds-cfg-plugin-type: preOperationModify
ds-cfg-invoke-for-internal-operations: true
dn: cn=UID Unique Attribute ,cn=Plugins,cn=config
changeType: modify
@@ -328,7 +344,11 @@
ds-cfg-plugin-type: preOperationAdd
ds-cfg-plugin-type: preOperationModify
ds-cfg-plugin-type: preOperationModifyDN
ds-cfg-plugin-type: postSynchronizationAdd
ds-cfg-plugin-type: postSynchronizationModify
ds-cfg-plugin-type: postSynchronizationModifyDN
ds-cfg-unique-attribute-type: bootParameter
ds-cfg-invoke-for-internal-operations: true
dn: cn=JKS,cn=Key Manager Providers,cn=config
changetype: modify
opends/tests/unit-tests-testng/src/server/org/opends/server/api/plugin/DirectoryServerPluginTestCase.java
@@ -37,9 +37,13 @@
import org.testng.annotations.Test;
import org.testng.annotations.BeforeClass;
import org.opends.server.admin.server.AdminTestCaseUtils;
import org.opends.server.admin.std.meta.PluginCfgDefn;
import org.opends.server.admin.std.server.PluginCfg;
import org.opends.server.plugins.NullPlugin;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.operation.*;
import org.opends.server.TestCaseUtils;
import org.opends.messages.Message;
@@ -464,6 +468,34 @@
    expectedPublicMethods.add(sigList);
    sigList = new LinkedList<String>();
    sigList.add("doPostSynchronization");
    sigList.add("void");
    sigList.add("org.opends.server.types.operation." +
                "PostSynchronizationAddOperation");
    expectedPublicMethods.add(sigList);
    sigList = new LinkedList<String>();
    sigList.add("doPostSynchronization");
    sigList.add("void");
    sigList.add("org.opends.server.types.operation." +
                "PostSynchronizationDeleteOperation");
    expectedPublicMethods.add(sigList);
    sigList = new LinkedList<String>();
    sigList.add("doPostSynchronization");
    sigList.add("void");
    sigList.add("org.opends.server.types.operation." +
                "PostSynchronizationModifyOperation");
    expectedPublicMethods.add(sigList);
    sigList = new LinkedList<String>();
    sigList.add("doPostSynchronization");
    sigList.add("void");
    sigList.add("org.opends.server.types.operation." +
                "PostSynchronizationModifyDNOperation");
    expectedPublicMethods.add(sigList);
    sigList = new LinkedList<String>();
    sigList.add("processSearchEntry");
    sigList.add("org.opends.server.api.plugin.SearchEntryPluginResult");
    sigList.add("org.opends.server.types.operation.SearchEntrySearchOperation");
@@ -498,7 +530,7 @@
    sigList = new LinkedList<String>();
    sigList.add("initializeInternal");
    sigList.add("void");
    sigList.add("org.opends.server.types.DN");
    sigList.add("org.opends.server.admin.std.server.PluginCfg");
    sigList.add("java.util.Set");
    expectedPublicMethods.add(sigList);
@@ -513,6 +545,17 @@
    expectedPublicMethods.add(sigList);
    sigList = new LinkedList<String>();
    sigList.add("invokeForInternalOperations");
    sigList.add("boolean");
    expectedPublicMethods.add(sigList);
    sigList = new LinkedList<String>();
    sigList.add("setInvokeForInternalOperations");
    sigList.add("void");
    sigList.add("boolean");
    expectedPublicMethods.add(sigList);
    sigList = new LinkedList<String>();
    sigList.add("getClass");
    sigList.add("java.lang.Class");
    expectedPublicMethods.add(sigList);
@@ -676,6 +719,69 @@
  public void testGetPluginEntryDN()
         throws Exception
  {
    Entry pluginEntry = TestCaseUtils.makeEntry(
      "dn: cn=Null Plugin,cn=Plugins,cn=config",
      "objectClass: top",
      "objectClass: ds-cfg-plugin",
      "cn: Null Plugin",
      "ds-cfg-plugin-class: org.opends.server.plugins.NullPlugin",
      "ds-cfg-plugin-enabled: true",
      "ds-cfg-plugin-type: startup",
      "ds-cfg-plugin-type: shutdown",
      "ds-cfg-plugin-type: postConnect",
      "ds-cfg-plugin-type: postDisconnect",
      "ds-cfg-plugin-type: ldifImport",
      "ds-cfg-plugin-type: ldifExport",
      "ds-cfg-plugin-type: preParseAbandon",
      "ds-cfg-plugin-type: preParseAdd",
      "ds-cfg-plugin-type: preParsebind",
      "ds-cfg-plugin-type: preParseCompare",
      "ds-cfg-plugin-type: preParseDelete",
      "ds-cfg-plugin-type: preParseExtended",
      "ds-cfg-plugin-type: preParseModify",
      "ds-cfg-plugin-type: preParseModifyDN",
      "ds-cfg-plugin-type: preParseSearch",
      "ds-cfg-plugin-type: preParseUnbind",
      "ds-cfg-plugin-type: preOperationAdd",
      "ds-cfg-plugin-type: preOperationbind",
      "ds-cfg-plugin-type: preOperationCompare",
      "ds-cfg-plugin-type: preOperationDelete",
      "ds-cfg-plugin-type: preOperationExtended",
      "ds-cfg-plugin-type: preOperationModify",
      "ds-cfg-plugin-type: preOperationModifyDN",
      "ds-cfg-plugin-type: preOperationSearch",
      "ds-cfg-plugin-type: postOperationAbandon",
      "ds-cfg-plugin-type: postOperationAdd",
      "ds-cfg-plugin-type: postOperationbind",
      "ds-cfg-plugin-type: postOperationCompare",
      "ds-cfg-plugin-type: postOperationDelete",
      "ds-cfg-plugin-type: postOperationExtended",
      "ds-cfg-plugin-type: postOperationModify",
      "ds-cfg-plugin-type: postOperationModifyDN",
      "ds-cfg-plugin-type: postOperationSearch",
      "ds-cfg-plugin-type: postOperationUnbind",
      "ds-cfg-plugin-type: postResponseAdd",
      "ds-cfg-plugin-type: postResponsebind",
      "ds-cfg-plugin-type: postResponseCompare",
      "ds-cfg-plugin-type: postResponseDelete",
      "ds-cfg-plugin-type: postResponseExtended",
      "ds-cfg-plugin-type: postResponseModify",
      "ds-cfg-plugin-type: postResponseModifyDN",
      "ds-cfg-plugin-type: postResponseSearch",
      "ds-cfg-plugin-type: postsynchronizationAdd",
      "ds-cfg-plugin-type: postsynchronizationDelete",
      "ds-cfg-plugin-type: postsynchronizationModify",
      "ds-cfg-plugin-type: postsynchronizationModifyDN",
      "ds-cfg-plugin-type: searchResultEntry",
      "ds-cfg-plugin-type: searchResultReference",
      "ds-cfg-plugin-type: subordinateModifyDN",
      "ds-cfg-plugin-type: intermediateResponse");
    PluginCfg configuration =
         AdminTestCaseUtils.getConfiguration(PluginCfgDefn.getInstance(),
                                             pluginEntry);
    NullPlugin nullPlugin = new NullPlugin();
    DN pluginEntryDN = DN.decode("cn=Null Plugin,cn=Plugins,cn=config");
@@ -685,7 +791,7 @@
      pluginTypes.add(t);
    }
    nullPlugin.initializeInternal(pluginEntryDN, pluginTypes);
    nullPlugin.initializeInternal(configuration, pluginTypes);
    assertEquals(nullPlugin.getPluginEntryDN(), pluginEntryDN);
  }
@@ -700,8 +806,70 @@
  public void testGetPluginTypes()
         throws Exception
  {
    Entry pluginEntry = TestCaseUtils.makeEntry(
      "dn: cn=Null Plugin,cn=Plugins,cn=config",
      "objectClass: top",
      "objectClass: ds-cfg-plugin",
      "cn: Null Plugin",
      "ds-cfg-plugin-class: org.opends.server.plugins.NullPlugin",
      "ds-cfg-plugin-enabled: true",
      "ds-cfg-plugin-type: startup",
      "ds-cfg-plugin-type: shutdown",
      "ds-cfg-plugin-type: postConnect",
      "ds-cfg-plugin-type: postDisconnect",
      "ds-cfg-plugin-type: ldifImport",
      "ds-cfg-plugin-type: ldifExport",
      "ds-cfg-plugin-type: preParseAbandon",
      "ds-cfg-plugin-type: preParseAdd",
      "ds-cfg-plugin-type: preParsebind",
      "ds-cfg-plugin-type: preParseCompare",
      "ds-cfg-plugin-type: preParseDelete",
      "ds-cfg-plugin-type: preParseExtended",
      "ds-cfg-plugin-type: preParseModify",
      "ds-cfg-plugin-type: preParseModifyDN",
      "ds-cfg-plugin-type: preParseSearch",
      "ds-cfg-plugin-type: preParseUnbind",
      "ds-cfg-plugin-type: preOperationAdd",
      "ds-cfg-plugin-type: preOperationbind",
      "ds-cfg-plugin-type: preOperationCompare",
      "ds-cfg-plugin-type: preOperationDelete",
      "ds-cfg-plugin-type: preOperationExtended",
      "ds-cfg-plugin-type: preOperationModify",
      "ds-cfg-plugin-type: preOperationModifyDN",
      "ds-cfg-plugin-type: preOperationSearch",
      "ds-cfg-plugin-type: postOperationAbandon",
      "ds-cfg-plugin-type: postOperationAdd",
      "ds-cfg-plugin-type: postOperationbind",
      "ds-cfg-plugin-type: postOperationCompare",
      "ds-cfg-plugin-type: postOperationDelete",
      "ds-cfg-plugin-type: postOperationExtended",
      "ds-cfg-plugin-type: postOperationModify",
      "ds-cfg-plugin-type: postOperationModifyDN",
      "ds-cfg-plugin-type: postOperationSearch",
      "ds-cfg-plugin-type: postOperationUnbind",
      "ds-cfg-plugin-type: postResponseAdd",
      "ds-cfg-plugin-type: postResponsebind",
      "ds-cfg-plugin-type: postResponseCompare",
      "ds-cfg-plugin-type: postResponseDelete",
      "ds-cfg-plugin-type: postResponseExtended",
      "ds-cfg-plugin-type: postResponseModify",
      "ds-cfg-plugin-type: postResponseModifyDN",
      "ds-cfg-plugin-type: postResponseSearch",
      "ds-cfg-plugin-type: postsynchronizationAdd",
      "ds-cfg-plugin-type: postsynchronizationDelete",
      "ds-cfg-plugin-type: postsynchronizationModify",
      "ds-cfg-plugin-type: postsynchronizationModifyDN",
      "ds-cfg-plugin-type: searchResultEntry",
      "ds-cfg-plugin-type: searchResultReference",
      "ds-cfg-plugin-type: subordinateModifyDN",
      "ds-cfg-plugin-type: intermediateResponse");
    PluginCfg configuration =
         AdminTestCaseUtils.getConfiguration(PluginCfgDefn.getInstance(),
                                             pluginEntry);
    NullPlugin nullPlugin = new NullPlugin();
    DN pluginEntryDN = DN.decode("cn=Null Plugin,cn=Plugins,cn=config");
    HashSet<PluginType> pluginTypes = new HashSet<PluginType>();
    for (PluginType t : PluginType.values())
@@ -709,7 +877,7 @@
      pluginTypes.add(t);
    }
    nullPlugin.initializeInternal(pluginEntryDN, pluginTypes);
    nullPlugin.initializeInternal(configuration, pluginTypes);
    assertEquals(nullPlugin.getPluginTypes(), pluginTypes);
  }
opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/InvocationCounterPlugin.java
@@ -89,6 +89,8 @@
  private static AtomicInteger preOperationCounter    = new AtomicInteger(0);
  private static AtomicInteger postOperationCounter   = new AtomicInteger(0);
  private static AtomicInteger postResponseCounter    = new AtomicInteger(0);
  private static AtomicInteger postSynchronizationCounter =
                                    new AtomicInteger(0);
  private static AtomicInteger searchEntryCounter     = new AtomicInteger(0);
  private static AtomicInteger searchReferenceCounter = new AtomicInteger(0);
  private static AtomicInteger subordinateModifyDNCounter =
@@ -704,6 +706,69 @@
   * {@inheritDoc}
   */
  @Override()
  public void doPostSynchronization(PostSynchronizationAddOperation
                                         addOperation)
  {
    postSynchronizationCounter.incrementAndGet();
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void doPostSynchronization(PostSynchronizationModifyOperation
                                         modifyOperation)
  {
    postSynchronizationCounter.incrementAndGet();
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void doPostSynchronization(PostSynchronizationModifyDNOperation
                                         modifyDNOperation)
  {
    postSynchronizationCounter.incrementAndGet();
  }
  /**
   * Retrieves the number of times that the post-synchronization plugins have
   * been called since the last reset.
   *
   * @return  The number of times that the post-synchronization plugins have
   *          been called since the last reset.
   */
  public static int getPostSynchronizationCount()
  {
    return postSynchronizationCounter.get();
  }
  /**
   * Resets the post-synchronization plugin invocation count to zero.
   *
   * @return  The post-synchronization plugin invocation count before it was
   *          reset.
   */
  public static int resetPostSynchronizationCount()
  {
    return postSynchronizationCounter.getAndSet(0);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public SearchEntryPluginResult
       processSearchEntry(SearchEntrySearchOperation searchOperation,
                          SearchResultEntry searchEntry)
@@ -1031,6 +1096,7 @@
    resetPreOperationCount();
    resetPostOperationCount();
    resetPostResponseCount();
    resetPostSynchronizationCount();
    resetSearchEntryCount();
    resetSearchReferenceCount();
    resetSubordinateModifyDNCount();