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

gbellato
23.38.2007 1b29f765bdfe3705c1b88fcdbb4fa923682e9678
This integrates the multi-master synchronization with the new admin framework (issue 1477)
and makes possible to dynamically add or remove changelog server and
synchronization domains in a running server (issue 639).

It was necessary to slightly modify the configuration :

- The objectclass ds-cfg-multimaster-synchronization-provider must be added to
the entry : cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config
- the domains must be configured under
cn=domains, cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config
instead of being directly under this entry.

The synchronization.ldif file has been updated to reflect these changes.
I will update the configuration doc in the wiki
6 files added
23 files modified
3329 ■■■■ changed files
opends/resource/config/synchronization.ldif 16 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif 4 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/ChangelogServerConfiguration.xml 172 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/MultimasterDomainConfiguration.xml 211 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/MultimasterSynchronizationProviderConfiguration.xml 33 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml 14 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/SynchronizationProviderConfiguration.xml 56 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/SynchronizationProvider.java 15 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java 16 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/SynchronizationProviderConfigManager.java 1061 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/synchronization/changelog/Changelog.java 299 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/synchronization/common/LogMessages.java 18 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/synchronization/plugin/ChangelogBroker.java 40 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/synchronization/plugin/ChangelogListener.java 136 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/synchronization/plugin/MultimasterSynchronization.java 240 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java 448 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java 3 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/InitOnLineTest.java 59 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ProtocolWindowTest.java 11 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ReSyncTest.java 11 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SchemaSynchronizationTest.java 14 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/StressTest.java 10 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationTestCase.java 141 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java 21 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/changelog/ChangelogFakeConfiguration.java 190 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/changelog/ChangelogTest.java 54 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/changelog/dbHandlerTest.java 17 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/plugin/HistoricalTest.java 18 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/plugin/ModifyConflictTest.java 1 ●●●● patch | view | raw | blame | history
opends/resource/config/synchronization.ldif
@@ -13,16 +13,22 @@
dn: cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config
objectClass: top
objectClass: ds-cfg-synchronization-provider
objectClass: ds-cfg-multimaster-synchronization-provider
ds-cfg-synchronization-provider-enabled: true
ds-cfg-synchronization-provider-class: org.opends.server.synchronization.plugin.MultimasterSynchronization
dn: cn=example, cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config
dn: cn=domains, cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config
objectClass: top
objectClass: ds-cfg-branch
cn: domains
dn: cn=example, cn=domains, cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config
objectClass: top
objectClass: ds-cfg-synchronization-provider-config
cn: example
ds-cfg-synchronization-dn: dc=example,dc=com
ds-cfg-changelog-server: host1:8989
ds-cfg-changelog-server: host2:8989
ds-cfg-changelog-server: localhost:8989
ds-cfg-changelog-server: localhost:8990
ds-cfg-directory-server-id: 1
ds-cfg-receive-status: true
@@ -31,7 +37,7 @@
objectClass: ds-cfg-synchronization-changelog-server-config
cn: Changelog Server
ds-cfg-changelog-port: 8989
ds-cfg-changelog-server: host1:8989
ds-cfg-changelog-server: host2:8989
ds-cfg-changelog-server: localhost:8989
ds-cfg-changelog-server: localhost:8990
ds-cfg-changelog-server-id: 1
opends/resource/schema/02-config.ldif
@@ -1634,6 +1634,10 @@
  MUST ( ds-task-initialize-domain-dn $ ds-task-initialize-replica-server-id )
  MAY ( ds-task-processed-entry-count $ ds-task-unprocessed-entry-count )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.94
  NAME 'ds-cfg-multimaster-synchronization-provider'
  SUP ds-cfg-synchronization-provider STRUCTURAL
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.95
  NAME 'ds-cfg-dictionary-password-validator' SUP ds-cfg-password-validator
  STRUCTURAL MUST ( ds-cfg-dictionary-file $ ds-cfg-case-sensitive-validation $
opends/src/admin/defn/org/opends/server/admin/std/ChangelogServerConfiguration.xml
New file
@@ -0,0 +1,172 @@
<?xml version="1.0" encoding="utf-8"?>
<adm:managed-object name="changelog-server"
  plural-name="changelog-servers" package="org.opends.server.admin.std"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    The
    <adm:user-friendly-name />
    is the server to which Multimaster Domain connects to publish and
    receive changes to or from other Multimaster Domains.
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.65</ldap:oid>
      <ldap:name>
        ds-cfg-synchronization-changelog-server-config
      </ldap:name>
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="changelog-server" multi-valued="true"
    mandatory="false">
    <adm:synopsis>
      Specifies the addresses of the changelog server to which this
      <adm:user-friendly-name />
      should try to connect at startup time.
    </adm:synopsis>
    <adm:description>
      Adresses must be specified using the syntax: hostname:port
    </adm:description>
    <adm:requires-admin-action>
      <adm:none />
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:string></adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.160</ldap:oid>
        <ldap:name>ds-cfg-changelog-server</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="changelog-server-id" multi-valued="false"
    mandatory="true" read-only="true">
    <adm:synopsis>
      Specifies the server ID of this Changelog Server
    </adm:synopsis>
    <adm:description>
      Each Changelog Server must have a different server ID.
    </adm:description>
    <adm:requires-admin-action>
      <adm:none />
    </adm:requires-admin-action>
    <adm:syntax>
      <adm:integer></adm:integer>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.278</ldap:oid>
        <ldap:name>ds-cfg-changelog-server-id</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="window-size" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the window size that will this Domain must use when
      communicating with changelog servers.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>100</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer></adm:integer>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.288</ldap:oid>
        <ldap:name>ds-cfg-window-size</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="queue-size" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the number of changes that will be kept in memory for
      each LDAP server in the topology.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>10000</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer></adm:integer>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.290</ldap:oid>
        <ldap:name>ds-cfg-changelog-max-queue-size</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="changelog-db-directory" mandatory="false"
    hidden="false" multi-valued="false" read-only="true">
    <adm:synopsis>
      The path where the
      <adm:user-friendly-name />
      will store all persistent information.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:string></adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.306</ldap:oid>
        <ldap:name>ds-cfg-changelog-db-directory</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="changelog-purge-delay" multi-valued="false">
    <adm:synopsis>
      The time (in seconds) after which the
      <adm:user-friendly-name />
      will erase all persistent information.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>86400s</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration base-unit="s" allow-unlimited="false" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.319</ldap:oid>
        <ldap:name>ds-cfg-changelog-purge-delay</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="changelog-port" mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      The port on which this
      <adm:user-friendly-name></adm:user-friendly-name>
      will wait for connections from other Changelog Servers or LDAP
      Servers or from LDAP servers.
    </adm:synopsis>
    <adm:requires-admin-action>
      <adm:none />
    </adm:requires-admin-action>
    <adm:syntax>
      <adm:integer></adm:integer>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.158</ldap:oid>
        <ldap:name>ds-cfg-changelog-port</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/MultimasterDomainConfiguration.xml
New file
@@ -0,0 +1,211 @@
<?xml version="1.0" encoding="utf-8"?>
<adm:managed-object name="multimaster-domain"
  plural-name="multimaster-domains"
  package="org.opends.server.admin.std"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    The
    <adm:user-friendly-name />
    is used to provide Multimaster Replication of several OpenDS copies
    of the same data
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.58</ldap:oid>
      <ldap:name>ds-cfg-synchronization-provider-config</ldap:name>
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="changelog-server" multi-valued="true"
    mandatory="true">
    <adm:synopsis>
      Specifies the addresses of the changelog server to which this
      <adm:user-friendly-name />
      should try to connect at startup time.
    </adm:synopsis>
    <adm:description>
      Adresses must be specified using the syntax: hostname:port
    </adm:description>
    <adm:requires-admin-action>
      <adm:none />
    </adm:requires-admin-action>
    <adm:syntax>
      <adm:string></adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.160</ldap:oid>
        <ldap:name>ds-cfg-changelog-server</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="server-id" multi-valued="false" mandatory="true"
    read-only="true">
    <adm:synopsis>
      Specifies the server ID of this multimaster provider.
    </adm:synopsis>
    <adm:description>
      Each multimaster provider must have a different server ID.
    </adm:description>
    <adm:requires-admin-action>
      <adm:none />
    </adm:requires-admin-action>
    <adm:syntax>
      <adm:integer></adm:integer>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.279</ldap:oid>
        <ldap:name>ds-cfg-directory-server-id</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="synchronization-dn" mandatory="true"
    multi-valued="false" read-only="true">
    <adm:synopsis>
      Specifies the base dn of the Multimaster Domain
    </adm:synopsis>
    <adm:syntax>
      <adm:dn></adm:dn>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.226</ldap:oid>
        <ldap:name>ds-cfg-synchronization-dn</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="max-receive-queue" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum length of the receive queue on the changelog
      server before flow control must be activated.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer></adm:integer>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.164</ldap:oid>
        <ldap:name>ds-cfg-max-receive-queue</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="max-receive-delay" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum delay on the changelog server before flow
      control must be activated.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0s</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration base-unit="s" allow-unlimited="false"
        upper-limit="2147483647" lower-limit="0" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.165</ldap:oid>
        <ldap:name>ds-cfg-max-receive-delay</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="max-send-queue" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum length of the send queue on the changelog
      server before flow control must be activated.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer></adm:integer>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.166</ldap:oid>
        <ldap:name>ds-cfg-max-send-queue</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="max-send-delay" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum send delay on the changelog server before
      flow control must be activated.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0s</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration upper-limit="2147483647" allow-unlimited="false"
        lower-limit="0" base-unit="s" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.167</ldap:oid>
        <ldap:name>ds-cfg-max-send-delay</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="window-size" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the window size that will this Domain must use when
      communicating with changelog servers.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>100</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer></adm:integer>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.288</ldap:oid>
        <ldap:name>ds-cfg-window-size</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="heartbeat-interval" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the heartbeat interval that this Domain must use when
      communicating with changelog servers. The Domain will expect
      regular heatbeat coming from the changelog server with this
      interval if they are not received it will close its connection and
      connect to another changelog server.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>1000s</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration base-unit="ms" allow-unlimited="false"
        lower-limit="100" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.305</ldap:oid>
        <ldap:name>ds-cfg-heartbeat-interval</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/MultimasterSynchronizationProviderConfiguration.xml
New file
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<adm:managed-object name="multimaster-synchronization-provider"
  plural-name="multimaster-synchronization-providers"
  package="org.opends.server.admin.std"
  extends="synchronization-provider"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap" abstract="false">
  <adm:synopsis>
    The
    <adm:user-friendly-name />
    is used to provide Multimaster Replication of several OpenDS copies
    of the same data
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.91</ldap:oid>
      <ldap:name>ds-cfg-multimaster-synchronization-provider</ldap:name>
      <ldap:superior>ds-cfg-synchronization-provider</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:relation name="multimaster-domain">
    <adm:one-to-many />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>cn=domains</ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:relation name="changelog-server">
    <adm:one-to-zero-or-one />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>cn=changelog server</ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
@@ -21,7 +21,15 @@
      </ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
    <adm:relation name="access-control-handler">
  <adm:relation name="synchronization-provider">
    <adm:one-to-many />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>
        cn=Synchronization Providers, cn=config
      </ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:relation name="access-control-handler">
    <adm:one-to-one />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>
@@ -72,9 +80,7 @@
  <adm:relation name="plugin">
    <adm:one-to-many />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>
        cn=Plugins,cn=config
      </ldap:rdn-sequence>
      <ldap:rdn-sequence>cn=Plugins,cn=config</ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:relation name="virtual-attribute">
opends/src/admin/defn/org/opends/server/admin/std/SynchronizationProviderConfiguration.xml
New file
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<adm:managed-object name="synchronization-provider"
  plural-name="synchronization-providers"
  package="org.opends.server.admin.std" abstract="true"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    <adm:user-friendly-plural-name />
    are responsible for handling Synchronization of the Directory Server
    datas with other OpenDS instances or other data repositories.
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.57</ldap:oid>
      <ldap:name>ds-cfg-synchronization-provider</ldap:name>
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="enabled" mandatory="true">
    <adm:synopsis>
      Indicate whether the
      <adm:user-friendly-name />
      is enabled for use.
    </adm:synopsis>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.155</ldap:oid>
        <ldap:name>ds-cfg-synchronization-provider-enabled</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="java-implementation-class" mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      The fully-qualified name of the Java class that provides the
      <adm:user-friendly-name />
      implementation.
    </adm:synopsis>
    <adm:syntax>
      <adm:java-class>
        <adm:instance-of>
          org.opends.server.api.SynchronizationProvider
        </adm:instance-of>
      </adm:java-class>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.154</ldap:oid>
        <ldap:name>ds-cfg-synchronization-provider-class</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/server/org/opends/server/api/SynchronizationProvider.java
@@ -30,7 +30,7 @@
import java.util.List;
import org.opends.server.config.ConfigEntry;
import org.opends.server.admin.std.server.SynchronizationProviderCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
@@ -51,8 +51,11 @@
 * the Directory Server are properly communicated to other instances,
 * and potentially to other kinds of applications, so that they can be
 * updated accordingly.
 *
 * @param <T> the configuration for the synchronization provider.
 */
public abstract class SynchronizationProvider
public abstract class
  SynchronizationProvider<T extends SynchronizationProviderCfg>
{
@@ -61,9 +64,8 @@
   * Performs any initialization that might be necessary for this
   * synchronization provider.
   *
   * @param  configEntry  The entry containing the configuration
   *                      information for this synchronization
   *                      provider.
   * @param  config  The configuration information for this
   *                 synchronization provider.
   *
   * @throws  ConfigException  If the provided entry does not contain
   *                           a valid configuration for this
@@ -75,8 +77,7 @@
   *                                   is not related to the server
   *                                   configuration.
   */
  public abstract void initializeSynchronizationProvider(
                            ConfigEntry configEntry)
  public abstract void initializeSynchronizationProvider(T config)
         throws ConfigException, InitializationException;
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -49,6 +49,7 @@
import org.opends.server.admin.ClassLoaderProvider;
import org.opends.server.admin.std.server.PasswordValidatorCfg;
import org.opends.server.admin.std.server.SynchronizationProviderCfg;
import org.opends.server.api.*;
import org.opends.server.api.plugin.PluginType;
import org.opends.server.api.plugin.StartupPluginResult;
@@ -334,7 +335,8 @@
  // The set of synchronization providers that have been registered with the
  // Directory Server.
  private CopyOnWriteArrayList<SynchronizationProvider>
  private
    CopyOnWriteArrayList<SynchronizationProvider<SynchronizationProviderCfg>>
               synchronizationProviders;
  // The set of virtual attributes defined in the server.
@@ -630,7 +632,8 @@
    directoryServer.shutdownListeners =
         new CopyOnWriteArrayList<ServerShutdownListener>();
    directoryServer.synchronizationProviders =
         new CopyOnWriteArrayList<SynchronizationProvider>();
         new CopyOnWriteArrayList<SynchronizationProvider
                                 <SynchronizationProviderCfg>>();
    directoryServer.supportedControls = new TreeSet<String>();
    directoryServer.supportedFeatures = new TreeSet<String>();
    directoryServer.virtualAttributes =
@@ -7187,8 +7190,9 @@
   * @return  The set of synchronization providers that have been registered
   *          with the Directory Server.
   */
  public static CopyOnWriteArrayList<SynchronizationProvider>
              getSynchronizationProviders()
  public static
    CopyOnWriteArrayList<SynchronizationProvider<SynchronizationProviderCfg>>
      getSynchronizationProviders()
  {
    return directoryServer.synchronizationProviders;
  }
@@ -7200,8 +7204,8 @@
   *
   * @param  provider  The synchronization provider to register.
   */
  public static void registerSynchronizationProvider(SynchronizationProvider
                                                          provider)
  public static void registerSynchronizationProvider(
      SynchronizationProvider<SynchronizationProviderCfg> provider)
  {
    directoryServer.synchronizationProviders.add(provider);
  }
opends/src/server/org/opends/server/core/SynchronizationProviderConfigManager.java
@@ -28,33 +28,31 @@
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.api.ConfigChangeListener;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.types.DebugLogLevel;
import static org.opends.server.loggers.Error.*;
import static org.opends.server.messages.ConfigMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.server.admin.ClassPropertyDefinition;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.server.ServerManagementContext;
import org.opends.server.admin.std.meta.SynchronizationProviderCfgDefn;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.admin.std.server.SynchronizationProviderCfg;
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.config.ConfigException;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
@@ -66,17 +64,20 @@
 * to them while the server is running.
 */
public class SynchronizationProviderConfigManager
       implements ConfigChangeListener, ConfigAddListener, ConfigDeleteListener
       implements ConfigurationChangeListener<SynchronizationProviderCfg>,
       ConfigurationAddListener<SynchronizationProviderCfg>,
       ConfigurationDeleteListener<SynchronizationProviderCfg>
{
  // The mapping between configuration entry DNs and their corresponding
  // synchronization provider implementations.
  private ConcurrentHashMap<DN,SynchronizationProvider> registeredProviders;
  private ConcurrentHashMap<DN,
    SynchronizationProvider<SynchronizationProviderCfg>> registeredProviders =
      new ConcurrentHashMap<DN,
        SynchronizationProvider<SynchronizationProviderCfg>>();
  // The DN of the associated configuration entry.
  private DN configEntryDN;
@@ -107,765 +108,407 @@
  public void initializeSynchronizationProviders()
         throws ConfigException, InitializationException
  {
    registeredProviders = new ConcurrentHashMap<DN,SynchronizationProvider>();
    // Create an internal server management context and retrieve
    // the root configuration which has the synchronization provider relation.
    ServerManagementContext context = ServerManagementContext.getInstance();
    RootCfg root = context.getRootConfiguration();
    // Register as an add and delete listener so that we can
    // be notified when new synchronization providers are added or existing
    // sycnhronization providers are removed.
    root.addSynchronizationProviderAddListener(this);
    root.addSynchronizationProviderDeleteListener(this);
    // Get the configuration entry that is the parent for all synchronization
    // providers in the server.
    ConfigEntry providerRoot;
    try
    // Initialize existing synchronization providers.
    for (String name : root.listSynchronizationProviders())
    {
      configEntryDN = DN.decode(DN_SYNCHRONIZATION_PROVIDER_BASE);
      providerRoot  = DirectoryServer.getConfigEntry(configEntryDN);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      // Get the synchronization provider's configuration.
      // This will automatically decode and validate its properties.
      SynchronizationProviderCfg config = root.getSynchronizationProvider(name);
      // Register as a change listener for this synchronization provider
      // entry so that we can be notified when it is disabled or enabled.
      config.addChangeListener(this);
      // Ignore this synchronization provider if it is disabled.
      if (config.isEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
        // Perform initialization, load the synchronization provider's
        // implementation class and initialize it.
        SynchronizationProvider<SynchronizationProviderCfg> provider =
          getSynchronizationProvider(config);
        // Register the synchronization provider with the Directory Server.
        DirectoryServer.registerSynchronizationProvider(provider);
        // Put this synchronization provider in the hash map so that we will be
        // able to find it if it is deleted or disabled.
        registeredProviders.put(config.dn(), provider);
      }
      int    msgID   = MSGID_CONFIG_SYNCH_CANNOT_GET_CONFIG_BASE;
      String message = getMessage(msgID, stackTraceToSingleLineString(e));
      throw new ConfigException(msgID, message, e);
    }
    // If the configuration root entry is null, then assume it doesn't exist.
    // In that case, then fail.  At least that entry must exist in the
    // configuration, even if there are no synchronization providers defined
    // below it.
    if (providerRoot == null)
    {
      int    msgID   = MSGID_CONFIG_SYNCH_BASE_DOES_NOT_EXIST;
      String message = getMessage(msgID);
      throw new ConfigException(msgID, message);
    }
    // Register as an add and delete listener for the base entry so that we can
    // be notified if new providers are added or existing providers are removed.
    providerRoot.registerAddListener(this);
    providerRoot.registerDeleteListener(this);
    // Iterate through the set of immediate children below the provider root
    // entry and register those providers.
    for (ConfigEntry providerEntry : providerRoot.getChildren().values())
    {
      DN providerDN = providerEntry.getDN();
      // Register as a change listener for this provider entry so that we will
      // be notified of any changes that may be made to it.
      providerEntry.registerChangeListener(this);
      // Check to see if this entry appears to contain a synchronization
      // provider configuration.  If not, then fail.
      try
      {
        SearchFilter providerFilter =
          SearchFilter.createFilterFromString("(objectClass=" +
                                              OC_SYNCHRONIZATION_PROVIDER +
                                              ")");
        if (! providerFilter.matchesEntry(providerEntry.getEntry()))
        {
          int msgID = MSGID_CONFIG_SYNCH_ENTRY_DOES_NOT_HAVE_PROVIDER_CONFIG;
          String message = getMessage(msgID, String.valueOf(providerDN));
          throw new ConfigException(msgID, message);
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        int    msgID   = MSGID_CONFIG_SYNCH_CANNOT_CHECK_FOR_PROVIDER_CONFIG_OC;
        String message = getMessage(msgID, String.valueOf(providerDN),
                                    stackTraceToSingleLineString(e));
        throw new InitializationException(msgID, message, e);
      }
      // See if the entry contains an attribute that indicates whether the
      // synchronization provider should be enabled.  If it does not, then fail.
      // If it is present but set to false, then log a warning and skip it.
      int msgID = MSGID_CONFIG_SYNCH_DESCRIPTION_PROVIDER_ENABLED;
      BooleanConfigAttribute enabledStub =
           new BooleanConfigAttribute(ATTR_SYNCHRONIZATION_PROVIDER_ENABLED,
                                      getMessage(msgID), true);
      try
      {
        BooleanConfigAttribute enabledAttr =
             (BooleanConfigAttribute)
             providerEntry.getConfigAttribute(enabledStub);
        if (enabledAttr == null)
        {
          msgID = MSGID_CONFIG_SYNCH_PROVIDER_NO_ENABLED_ATTR;
          String message = getMessage(msgID, String.valueOf(providerDN));
          throw new ConfigException(msgID, message);
        }
        else if (! enabledAttr.activeValue())
        {
          msgID = MSGID_CONFIG_SYNCH_PROVIDER_DISABLED;
          String message = getMessage(msgID, String.valueOf(providerDN));
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_WARNING, message, msgID);
          continue;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_DETERMINE_ENABLED_STATE;
        String message = getMessage(msgID, String.valueOf(providerDN),
                                    stackTraceToSingleLineString(e));
        throw new InitializationException(msgID, message, e);
      }
      // See if the entry contains an attribute that specifies the class name
      // for the synchronization provider implementation.  If there  is no such
      // attribute, then fail.
      String providerClassName;
      msgID = MSGID_CONFIG_SYNCH_DESCRIPTION_PROVIDER_CLASS;
      StringConfigAttribute classStub =
           new StringConfigAttribute(ATTR_SYNCHRONIZATION_PROVIDER_CLASS,
                                     getMessage(msgID), true, false, true);
      try
      {
        StringConfigAttribute classAttr =
             (StringConfigAttribute)
             providerEntry.getConfigAttribute(classStub);
        if (classAttr == null)
        {
          msgID = MSGID_CONFIG_SYNCH_NO_CLASS_ATTR;
          String message = getMessage(msgID, String.valueOf(providerDN));
          throw new ConfigException(msgID, message);
        }
        else
        {
          providerClassName = classAttr.activeValue();
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_DETERMINE_CLASS;
        String message = getMessage(msgID, String.valueOf(providerDN),
                                    stackTraceToSingleLineString(e));
        throw new InitializationException(msgID, message, e);
      }
      // Load the specified provider class.  If an error occurs, then fail.
      Class providerClass;
      try
      {
        providerClass = DirectoryServer.loadClass(providerClassName);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_LOAD_PROVIDER_CLASS;
        String message = getMessage(msgID, String.valueOf(providerClassName),
                                    String.valueOf(providerDN),
                                    stackTraceToSingleLineString(e));
        throw new InitializationException(msgID, message, e);
      }
      // Make sure that the specified class is a valid synchronization provider.
      // If not, then fail.
      SynchronizationProvider provider;
      try
      {
        provider = (SynchronizationProvider) providerClass.newInstance();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_INSTANTIATE_PROVIDER;
        String message = getMessage(msgID, String.valueOf(providerClassName),
                                    String.valueOf(providerDN),
                                    stackTraceToSingleLineString(e));
        throw new InitializationException(msgID, message, e);
      }
      // Perform the necessary initialization for the synchronization provider.
      // If a problem occurs, then fail.
      try
      {
        provider.initializeSynchronizationProvider(providerEntry);
      }
      catch (ConfigException ce)
      {
        msgID = MSGID_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER;
        String message = getMessage(msgID, String.valueOf(providerDN),
                                    ce.getMessage());
        throw new ConfigException(msgID, message, ce);
      }
      catch (InitializationException ie)
      {
        msgID = MSGID_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER;
        String message = getMessage(msgID, String.valueOf(providerDN),
                                    ie.getMessage());
        throw new InitializationException(msgID, message, ie);
      }
      catch (Exception e)
      {
        msgID = MSGID_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER;
        String message = getMessage(msgID, String.valueOf(providerDN),
                                    stackTraceToSingleLineString(e));
        throw new ConfigException(msgID, message, e);
      }
      // Register the synchronization provider with the Directory Server.
      DirectoryServer.registerSynchronizationProvider(provider);
      // Put this provider in the hash so that we will be able to find it if it
      // is altered.
      registeredProviders.put(providerDN, provider);
    }
  }
  /**
   * Indicates whether the configuration entry that will result from a proposed
   * modification is acceptable to this change listener.
   *
   * @param  configEntry         The configuration entry that will result from
   *                             the requested update.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed change is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry contains an acceptable
   *          configuration, or <CODE>false</CODE> if it does not.
   * {@inheritDoc}
   */
  public boolean configChangeIsAcceptable(ConfigEntry configEntry,
                                          StringBuilder unacceptableReason)
  public ConfigChangeResult applyConfigurationChange(
      SynchronizationProviderCfg configuration)
  {
    DN providerDN = configEntry.getDN();
    SynchronizationProvider provider = registeredProviders.get(providerDN);
    // Check to see if this entry appears to contain a backend configuration.
    // If not, then reject it.
    try
    {
      SearchFilter providerFilter =
           SearchFilter.createFilterFromString("(objectClass=" +
                                               OC_SYNCHRONIZATION_PROVIDER +
                                               ")");
      if (! providerFilter.matchesEntry(configEntry.getEntry()))
      {
        int msgID = MSGID_CONFIG_SYNCH_ENTRY_DOES_NOT_HAVE_PROVIDER_CONFIG;
        unacceptableReason.append(getMessage(msgID,
                                             String.valueOf(providerDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_CONFIG_SYNCH_CANNOT_CHECK_FOR_PROVIDER_CONFIG_OC;
      unacceptableReason.append(getMessage(msgID, String.valueOf(providerDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that indicates whether the
    // provider should be enabled.  If it does not, then reject it.
    int msgID = MSGID_CONFIG_SYNCH_DESCRIPTION_PROVIDER_ENABLED;
    BooleanConfigAttribute enabledStub =
         new BooleanConfigAttribute(ATTR_SYNCHRONIZATION_PROVIDER_ENABLED,
                                    getMessage(msgID), true);
    try
    {
      BooleanConfigAttribute enabledAttr =
           (BooleanConfigAttribute) configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        msgID = MSGID_CONFIG_SYNCH_PROVIDER_NO_ENABLED_ATTR;
        unacceptableReason.append(getMessage(msgID,
                                             String.valueOf(providerDN)));
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_DETERMINE_ENABLED_STATE;
      unacceptableReason.append(getMessage(msgID,
                                           String.valueOf(providerDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // See if the entry contains an attribute that specifies the provider class.
    // If it does not, then fail.
    String className;
    msgID = MSGID_CONFIG_SYNCH_DESCRIPTION_PROVIDER_CLASS;
    StringConfigAttribute classStub =
         new StringConfigAttribute(ATTR_SYNCHRONIZATION_PROVIDER_CLASS,
                                   getMessage(msgID), true, false, true);
    try
    {
      StringConfigAttribute classAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classAttr == null)
      {
        msgID = MSGID_CONFIG_SYNCH_NO_CLASS_ATTR;
        unacceptableReason.append(getMessage(msgID,
                                             String.valueOf(providerDN)));
        return false;
      }
      else
      {
        className = classAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_DETERMINE_CLASS;
      unacceptableReason.append(getMessage(msgID,
                                           String.valueOf(providerDN),
                                           stackTraceToSingleLineString(e)));
      return false;
    }
    // If the provider is currently disabled, or if the class is different from
    // the one used by the running provider, then make sure that it is
    // acceptable.
    if ((provider == null) ||
        (! className.equals(provider.getClass().getName())))
    {
      Class providerClass;
      try
      {
        providerClass = DirectoryServer.loadClass(className);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_LOAD_PROVIDER_CLASS;
        unacceptableReason.append(getMessage(msgID, String.valueOf(className),
                                             String.valueOf(providerDN),
                                             stackTraceToSingleLineString(e)));
        return false;
      }
      try
      {
        SynchronizationProvider newProvider =
             (SynchronizationProvider) providerClass.newInstance();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_INSTANTIATE_PROVIDER;
        unacceptableReason.append(getMessage(msgID, String.valueOf(className),
                                             String.valueOf(providerDN),
                                             stackTraceToSingleLineString(e)));
        return false;
      }
    }
    // If we've gotten to this point, then it is acceptable as far as we are
    // concerned.  If it is unacceptable according to the configuration for that
    // synchronization provider, then the provider itself will need to make that
    // determination.
    return true;
  }
  /**
   * Attempts to apply a new configuration to this Directory Server component
   * based on the provided changed entry.
   *
   * @param  configEntry  The configuration entry that containing the updated
   *                      configuration for this component.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   */
  public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry)
  {
    DN providerDN = configEntry.getDN();
    SynchronizationProvider provider = registeredProviders.get(providerDN);
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<String> messages = new ArrayList<String>();
    // Attempt to get the existing synchronization provider. This will only
    // succeed if it is currently enabled.
    DN dn = configuration.dn();
    SynchronizationProvider<SynchronizationProviderCfg> provider =
      registeredProviders.get(dn);
    // Check to see if this entry appears to contain a synchronization provider
    // configuration.  If not, then fail.
    try
    // See whether the synchronization provider should be enabled.
    if (provider == null)
    {
      SearchFilter providerFilter =
           SearchFilter.createFilterFromString("(objectClass=" +
                                               OC_SYNCHRONIZATION_PROVIDER +
                                               ")");
      if (! providerFilter.matchesEntry(configEntry.getEntry()))
      if (configuration.isEnabled())
      {
        int msgID = MSGID_CONFIG_SYNCH_ENTRY_DOES_NOT_HAVE_PROVIDER_CONFIG;
        messages.add(getMessage(msgID, String.valueOf(providerDN)));
        if (resultCode == ResultCode.SUCCESS)
        // The synchronization provider needs to be enabled. Load, initialize,
        // and register the synchronization provider as per the add listener
        // method.
        try
        {
          resultCode = ResultCode.CONSTRAINT_VIOLATION;
          // Perform initialization, load the synchronization provider's
          // implementation class and initialize it.
          provider = getSynchronizationProvider(configuration);
          // Register the synchronization provider with the Directory Server.
          DirectoryServer.registerSynchronizationProvider(provider);
          // Put this synchronization provider in the hash map so that we will
          // be able to find it if it is deleted or disabled.
          registeredProviders.put(configuration.dn(), provider);
        }
        catch (ConfigException e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
            messages.add(e.getMessage());
            resultCode = DirectoryServer.getServerErrorResultCode();
          }
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
          int msgID = MSGID_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER;
          messages.add(getMessage(msgID, String.valueOf(
                             configuration.getJavaImplementationClass()),
                             String.valueOf(configuration.dn()),
                             stackTraceToSingleLineString(e)));
          resultCode = DirectoryServer.getServerErrorResultCode();
        }
      }
    }
    catch (Exception e)
    else
    {
      if (debugEnabled())
      if (configuration.isEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = MSGID_CONFIG_SYNCH_CANNOT_CHECK_FOR_PROVIDER_CONFIG_OC;
      messages.add(getMessage(msgID, String.valueOf(providerDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    // See if the entry contains an attribute that indicates whether the
    // provider should be enabled.  If it does not, then reject it.
    boolean shouldEnable = false;
    int msgID = MSGID_CONFIG_SYNCH_DESCRIPTION_PROVIDER_ENABLED;
    BooleanConfigAttribute enabledStub =
         new BooleanConfigAttribute(ATTR_SYNCHRONIZATION_PROVIDER_ENABLED,
                                    getMessage(msgID), true);
    try
    {
      BooleanConfigAttribute enabledAttr =
           (BooleanConfigAttribute) configEntry.getConfigAttribute(enabledStub);
      if (enabledAttr == null)
      {
        msgID = MSGID_CONFIG_SYNCH_PROVIDER_NO_ENABLED_ATTR;
        messages.add(getMessage(msgID, String.valueOf(providerDN)));
        if (resultCode == ResultCode.SUCCESS)
        // The synchronization provider is currently active, so we don't
        // need to do anything. Changes to the class name cannot be
        // applied dynamically, so if the class name did change then
        // indicate that administrative action is required for that
        // change to take effect.
        String className = configuration.getJavaImplementationClass();
        if (!className.equals(provider.getClass().getName()))
        {
          resultCode = ResultCode.CONSTRAINT_VIOLATION;
          adminActionRequired = true;
        }
      }
      else
      {
        shouldEnable = enabledAttr.pendingValue();
        // The connection handler is being disabled so remove it from
        // the DirectorySerevr list, shut it down and  remove it from the
        // hash map.
        DirectoryServer.deregisterSynchronizationProvider(provider);
        provider.finalizeSynchronizationProvider();
        registeredProviders.remove(dn);
      }
    }
    catch (Exception e)
    // Return the configuration result.
    return new ConfigChangeResult(resultCode, adminActionRequired,
        messages);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
      SynchronizationProviderCfg configuration,
      List<String> unacceptableReasons)
  {
    if (configuration.isEnabled())
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_DETERMINE_ENABLED_STATE;
      messages.add(getMessage(msgID, String.valueOf(providerDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      // It's enabled so always validate the class.
      return isJavaClassAcceptable(configuration, unacceptableReasons);
    } else
    {
      // It's disabled so ignore it.
      return true;
    }
  }
    // See if the entry contains an attribute that specifies the provider class.
    // If it does not, then reject it.
    String className = null;
    msgID = MSGID_CONFIG_SYNCH_DESCRIPTION_PROVIDER_CLASS;
    StringConfigAttribute classStub =
         new StringConfigAttribute(ATTR_SYNCHRONIZATION_PROVIDER_CLASS,
                                   getMessage(msgID), true, false, true);
    try
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(
    SynchronizationProviderCfg configuration)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<String> messages = new ArrayList<String>();
    // Register as a change listener for this synchronization provider entry
    // so that we will be notified if when it is disabled or enabled.
    configuration.addChangeListener(this);
    // Ignore this synchronization provider if it is disabled.
    if (configuration.isEnabled())
    {
      StringConfigAttribute classAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(classStub);
      if (classAttr == null)
      {
        msgID = MSGID_CONFIG_SYNCH_NO_CLASS_ATTR;
        messages.add(getMessage(msgID, String.valueOf(providerDN)));
        if (resultCode == ResultCode.SUCCESS)
        {
          resultCode = ResultCode.CONSTRAINT_VIOLATION;
        }
      }
      else
      {
        className = classAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_DETERMINE_CLASS;
      messages.add(getMessage(msgID, String.valueOf(providerDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    // If the provider is currently disabled, or if the class is different from
    // the one used by the running provider, then make sure that it is
    // acceptable.
    SynchronizationProvider newProvider = null;
    if ((resultCode == ResultCode.SUCCESS) &&
        ((provider == null) ||
         (! provider.getClass().getName().equals(className))))
    {
      Class providerClass = null;
      try
      {
        providerClass = DirectoryServer.loadClass(className);
        // Perform initialization, load the synchronization provider's
        // implementation class and initialize it.
        SynchronizationProvider<SynchronizationProviderCfg> provider =
          getSynchronizationProvider(configuration);
        // Register the synchronization provider with the Directory Server.
        DirectoryServer.registerSynchronizationProvider(provider);
        // Put this synchronization provider in the hash map so that we will be
        // able to find it if it is deleted or disabled.
        registeredProviders.put(configuration.dn(), provider);
      }
      catch (Exception e)
      catch (ConfigException e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_LOAD_PROVIDER_CLASS;
        messages.add(getMessage(msgID, String.valueOf(className),
                                String.valueOf(providerDN),
                                stackTraceToSingleLineString(e)));
        if (resultCode == ResultCode.SUCCESS)
        {
          messages.add(e.getMessage());
          resultCode = DirectoryServer.getServerErrorResultCode();
        }
      }
      try
      {
        if (providerClass != null)
        {
          newProvider = (SynchronizationProvider) providerClass.newInstance();
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_INSTANTIATE_PROVIDER;
        messages.add(getMessage(msgID, String.valueOf(className),
                                String.valueOf(providerDN),
                                stackTraceToSingleLineString(e)));
        if (resultCode == ResultCode.SUCCESS)
        {
          resultCode = DirectoryServer.getServerErrorResultCode();
        }
        int msgID = MSGID_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER;
        messages.add(getMessage(msgID, String.valueOf(
                           configuration.getJavaImplementationClass()),
                           String.valueOf(configuration.dn()),
                           stackTraceToSingleLineString(e)));
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    // Return the configuration result.
    return new ConfigChangeResult(resultCode, adminActionRequired,
        messages);
  }
    // If everything looks OK, then process the configuration change.
    if (resultCode == ResultCode.SUCCESS)
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationAddAcceptable(
      SynchronizationProviderCfg configuration,
      List<String> unacceptableReasons)
  {
    if (configuration.isEnabled())
    {
      // If the provider is currently disabled but should be enabled, then do
      // so now.
      if (provider == null)
      {
        if (shouldEnable && (newProvider != null))
        {
          try
          {
            newProvider.initializeSynchronizationProvider(configEntry);
            registeredProviders.put(configEntryDN, newProvider);
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, e);
            }
            msgID = MSGID_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER;
            messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                                    stackTraceToSingleLineString(e)));
            if (resultCode == ResultCode.SUCCESS)
            {
              resultCode = DirectoryServer.getServerErrorResultCode();
            }
          }
        }
      }
      // It's enabled so always validate the class.
      return isJavaClassAcceptable(configuration, unacceptableReasons);
    } else
    {
      // It's disabled so ignore it.
      return true;
    }
  }
      // Otherwise, see if the enabled flag or class name changed and indicate
      // that it will require a restart to take effect.
      else
      {
        if (! shouldEnable)
        {
          msgID = MSGID_CONFIG_SYNCH_PROVIDER_HAS_BEEN_DISABLED;
          messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
          adminActionRequired = true;
        }
        if (! provider.getClass().getName().equals(className))
        {
          msgID = MSGID_CONFIG_SYNCH_PROVIDER_CLASS_CHANGED;
          messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                                  String.valueOf(provider.getClass().getName()),
                                  String.valueOf(className)));
          adminActionRequired = true;
        }
      }
  /**
   * Check if the class provided in the configuration is an acceptable
   * java class for a synchronization provider.
   *
   * @param configuration       The configuration for which the class must be
   *                            checked.
   * @param unacceptableReasons A list containing the reasons why the class is
   *                            not acceptable.
   *
   * @return                    true if the class is acceptable or false if not.
   */
  @SuppressWarnings("unchecked")
  private SynchronizationProvider<SynchronizationProviderCfg>
    getSynchronizationProvider(SynchronizationProviderCfg configuration)
    throws ConfigException
  {
    String className = configuration.getJavaImplementationClass();
    SynchronizationProviderCfgDefn d =
      SynchronizationProviderCfgDefn.getInstance();
    ClassPropertyDefinition pd =
      d.getJavaImplementationClassPropertyDefinition();
    // Load the class
    Class<? extends SynchronizationProvider> theClass;
    SynchronizationProvider<SynchronizationProviderCfg> provider;
    try
    {
       theClass = pd.loadClass(className, SynchronizationProvider.class);
    } catch (Exception e)
    {
       // Handle the exception: put a message in the unacceptable reasons.
       int msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_LOAD_PROVIDER_CLASS;
       String message = getMessage(msgID, String.valueOf(className),
                                   String.valueOf(configuration.dn()),
                                   stackTraceToSingleLineString(e));
       throw new ConfigException(msgID, message, e);
    }
    try
    {
      // Instantiate the class.
      provider = theClass.newInstance();
    } catch (Exception e)
    {
      // Handle the exception: put a message in the unacceptable reasons.
      int msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_INSTANTIATE_PROVIDER;
      String message = getMessage(msgID, String.valueOf(className),
                                  String.valueOf(configuration.dn()),
                                  stackTraceToSingleLineString(e));
      throw new ConfigException(msgID, message, e);
    }
    try
    {
      // Initialize the Synchronization Provider.
      provider.initializeSynchronizationProvider(configuration);
    } catch (Exception e)
    {
      // Handle the exception: put a message in the unacceptable reasons.
      int msgID = MSGID_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER;
      String message = getMessage(msgID, String.valueOf(className),
                                  String.valueOf(configuration.dn()),
                                  stackTraceToSingleLineString(e));
      throw new ConfigException(msgID, message, e);
    }
    return provider;
  }
  /**
   * Check if the class provided in the configuration is an acceptable
   * java class for a synchronization provider.
   *
   * @param configuration       The configuration for which the class must be
   *                            checked.
   * @param unacceptableReasons A list containing the reasons why the class is
   *                            not acceptable.
   *
   * @return                    true if the class is acceptable or false if not.
   */
  private boolean isJavaClassAcceptable(
      SynchronizationProviderCfg configuration,
      List<String> unacceptableReasons)
  {
    String className = configuration.getJavaImplementationClass();
    SynchronizationProviderCfgDefn d =
      SynchronizationProviderCfgDefn.getInstance();
    ClassPropertyDefinition pd =
      d.getJavaImplementationClassPropertyDefinition();
    // Load the class and cast it to a synchronizationProvider.
    Class<? extends SynchronizationProvider> theClass;
    try
    {
       theClass = pd.loadClass(className, SynchronizationProvider.class);
       theClass.newInstance();
    } catch (Exception e)
    {
       // Handle the exception: put a message in the unacceptable reasons.
       int msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_LOAD_PROVIDER_CLASS;
       String message = getMessage(msgID, String.valueOf(className),
                                   String.valueOf(configuration.dn()),
                                   stackTraceToSingleLineString(e));
       unacceptableReasons.add(message);
       return false;
    }
    // Check that the implementation class implements the correct interface.
    try
    {
      // Determine the initialization method to use: it must take a
      // single parameter which is the exact type of the configuration
      // object.
      theClass.getMethod("initializeSynchronizationProvider",
                     configuration.definition().getServerConfigurationClass());
    } catch (Exception e)
    {
      // Handle the exception: put a message in the unacceptable reasons.
      int msgID = MSGID_CONFIG_SYNCH_UNABLE_TO_INSTANTIATE_PROVIDER;
      String message = getMessage(msgID, String.valueOf(className),
                                  String.valueOf(configuration.dn()),
                                  stackTraceToSingleLineString(e));
      unacceptableReasons.add(message);
      return false;
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * Indicates whether the configuration entry that will result from a proposed
   * add is acceptable to this add listener.
   *
   * @param  configEntry         The configuration entry that will result from
   *                             the requested add.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed entry is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry contains an acceptable
   *          configuration, or <CODE>false</CODE> if it does not.
   */
  public boolean configAddIsAcceptable(ConfigEntry configEntry,
                                       StringBuilder unacceptableReason)
  {
    // NYI
    // The class is valid as far as we can tell.
    return true;
  }
  /**
   * Attempts to apply a new configuration based on the provided added entry.
   *
   * @param  configEntry  The new configuration entry that contains the
   *                      configuration to apply.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry)
  public ConfigChangeResult applyConfigurationDelete(
      SynchronizationProviderCfg configuration)
  {
    // NYI
    return null;
    //  Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    // See if the entry is registered as a synchronization provider. If so,
    // deregister and stop it.
    DN dn = configuration.dn();
    SynchronizationProvider provider = registeredProviders.get(dn);
    if (provider != null)
    {
      DirectoryServer.deregisterSynchronizationProvider(provider);
      provider.finalizeSynchronizationProvider();
    }
    return new ConfigChangeResult(resultCode, adminActionRequired);
  }
  /**
   * Indicates whether it is acceptable to remove the provided configuration
   * entry.
   *
   * @param  configEntry         The configuration entry that will be removed
   *                             from the configuration.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed delete is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry may be removed from the
   *          configuration, or <CODE>false</CODE> if not.
   * {@inheritDoc}
   */
  public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
                                          StringBuilder unacceptableReason)
  public boolean isConfigurationDeleteAcceptable(
      SynchronizationProviderCfg configuration,
      List<String> unacceptableReasons)
  {
    // NYI
    // A delete should always be acceptable, so just return true.
    return true;
  }
  /**
   * Attempts to apply a new configuration based on the provided deleted entry.
   *
   * @param  configEntry  The new configuration entry that has been deleted.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   */
  public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry)
  {
    // NYI
    return null;
  }
}
opends/src/server/org/opends/server/synchronization/changelog/Changelog.java
@@ -40,26 +40,24 @@
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.ChangelogServerCfg;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.api.DirectoryThread;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.IntegerConfigAttribute;
import org.opends.server.config.IntegerWithUnitConfigAttribute;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.core.DirectoryServer;
import org.opends.server.messages.MessageHandler;
import org.opends.server.synchronization.protocol.SocketSession;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.ResultCode;
import com.sleepycat.je.DatabaseException;
@@ -73,7 +71,9 @@
 *
 * It is responsible for creating the changelog cache and managing it
 */
public class Changelog implements Runnable, ConfigurableComponent
public class Changelog
  implements Runnable, ConfigurableComponent,
             ConfigurationChangeListener<ChangelogServerCfg>
{
  private short serverId;
  private String serverURL;
@@ -85,7 +85,7 @@
  private boolean runListen = true;
  /* The list of changelog servers configured by the administrator */
  private List<String> changelogServers;
  private Collection<String> changelogServers;
  /* This table is used to store the list of dn for which we are currently
   * handling servers.
@@ -106,212 +106,30 @@
  private long trimAge; // the time (in sec) after which the  changes must
                        // de deleted from the persistent storage.
  static final String CHANGELOG_SERVER_ATTR = "ds-cfg-changelog-server";
  static final String SERVER_ID_ATTR = "ds-cfg-changelog-server-id";
  static final String CHANGELOG_PORT_ATTR = "ds-cfg-changelog-port";
  static final String WINDOW_SIZE_ATTR = "ds-cfg-window-size";
  static final String QUEUE_SIZE_ATTR = "ds-cfg-changelog-max-queue-size";
  static final String CHANGELOG_DIR_PATH_ATTR = "ds-cfg-changelog-db-directory";
  static final String PURGE_DELAY_ATTR = "ds-cfg-changelog-purge-delay";
  static final IntegerConfigAttribute changelogPortStub =
    new IntegerConfigAttribute(CHANGELOG_PORT_ATTR, "changelog port",
      true, false, false, true, 0, true, 65535);
  static final IntegerConfigAttribute serverIdStub =
    new IntegerConfigAttribute(SERVER_ID_ATTR, "server ID", true, false,
        false, true, 0, true, 65535);
  static final StringConfigAttribute changelogStub =
    new StringConfigAttribute(CHANGELOG_SERVER_ATTR,
        "changelog server information", true, true, false);
  static final IntegerConfigAttribute windowStub =
    new IntegerConfigAttribute(WINDOW_SIZE_ATTR, "window size",
                               false, false, false, true, 0, false, 0);
  static final IntegerConfigAttribute queueSizeStub =
    new IntegerConfigAttribute(QUEUE_SIZE_ATTR, "changelog queue size",
                               false, false, false, true, 0, false, 0);
  static final StringConfigAttribute dbDirnameStub =
    new StringConfigAttribute(CHANGELOG_DIR_PATH_ATTR,
        "changelog storage directory path", false, false, true);
  /**
   * The set of time units that will be used for expressing the
   * changelog purge delay.
   */
  private static final LinkedHashMap<String,Double> purgeTimeUnits =
       new LinkedHashMap<String,Double>();
  static
  {
    purgeTimeUnits.put(TIME_UNIT_SECONDS_ABBR, 1D);
    purgeTimeUnits.put(TIME_UNIT_SECONDS_FULL, 1D);
    purgeTimeUnits.put(TIME_UNIT_MINUTES_ABBR, 60D);
    purgeTimeUnits.put(TIME_UNIT_MINUTES_FULL, 1D);
    purgeTimeUnits.put(TIME_UNIT_HOURS_ABBR, 60*60D);
    purgeTimeUnits.put(TIME_UNIT_HOURS_FULL, 60*60D);
    purgeTimeUnits.put(TIME_UNIT_DAYS_ABBR, 24*60*60D);
    purgeTimeUnits.put(TIME_UNIT_DAYS_FULL, 24*60*60D);
  }
  static final IntegerWithUnitConfigAttribute purgeDelayStub =
    new IntegerWithUnitConfigAttribute(PURGE_DELAY_ATTR,
        "changelog purge delay", false, purgeTimeUnits, true, 0, false, 0);
  /**
   * Check if a ConfigEntry is valid.
   * @param config The config entry that needs to be checked.
   * @param unacceptableReason A description of the reason why the config entry
   *                           is not acceptable (if return is false).
   * @return a boolean indicating if the configEntry is valid.
   */
  public static boolean checkConfigEntry(ConfigEntry config,
      StringBuilder unacceptableReason)
  {
    try
    {
      IntegerConfigAttribute changelogPortAttr;
      changelogPortAttr =
        (IntegerConfigAttribute) config.getConfigAttribute(changelogPortStub);
      /* The config must provide a changelog port number
       */
      if (changelogPortAttr == null)
      {
        unacceptableReason.append(
            MessageHandler.getMessage(MSGID_NEED_CHANGELOG_PORT,
            config.getDN().toString())  );
      }
      /*
       * read the server Id information
       * this is a single valued integer, its value must fit on a
       * short integer
       */
      IntegerConfigAttribute serverIdAttr =
        (IntegerConfigAttribute) config.getConfigAttribute(serverIdStub);
      if (serverIdAttr == null)
      {
        unacceptableReason.append(
            MessageHandler.getMessage(MSGID_NEED_SERVER_ID,
            config.getDN().toString()) );
      }
      return true;
    } catch (ConfigException e)
    {
      return false;
    }
  }
  /**
   * Creates a new Changelog using the provided configuration entry.
   *
   * @param config The configuration entry where configuration can be found.
   * @throws ConfigException When Configuration entry is invalid.
   * @param configuration The configuration of this changelog.
   * @throws ConfigException When Configuration is invalid.
   */
  public Changelog(ConfigEntry config) throws ConfigException
  public Changelog(ChangelogServerCfg configuration) throws ConfigException
  {
    shutdown = false;
    runListen = true;
    IntegerConfigAttribute changelogPortAttr =
      (IntegerConfigAttribute) config.getConfigAttribute(changelogPortStub);
    /* if there is no changelog port configured, this process must not be a
     * changelog server
     */
    if (changelogPortAttr == null)
    {
      throw new ConfigException(MSGID_NEED_CHANGELOG_PORT,
          MessageHandler.getMessage(MSGID_NEED_CHANGELOG_PORT,
              config.getDN().toString())  );
    }
    int changelogPort = changelogPortAttr.activeIntValue();
    configAttributes.add(changelogPortAttr);
    /*
     * read the server Id information
     * this is a single valued integer, its value must fit on a
     * short integer
     */
    IntegerConfigAttribute serverIdAttr =
      (IntegerConfigAttribute) config.getConfigAttribute(serverIdStub);
    if (serverIdAttr == null)
    {
      throw new ConfigException(MSGID_NEED_SERVER_ID,
          MessageHandler.getMessage(MSGID_NEED_SERVER_ID,
              config.getDN().toString())  );
    }
    changelogServerId = (short) serverIdAttr.activeIntValue();
    configAttributes.add(serverIdAttr);
    /*
     * read the centralized changelog server configuration
     * this is a multivalued attribute
     */
    StringConfigAttribute changelogServer =
      (StringConfigAttribute) config.getConfigAttribute(changelogStub);
    changelogServers = new ArrayList<String>();
    if (changelogServer != null)
    {
      for (String serverURL : changelogServer.activeValues())
      {
        String[] splitStrings = serverURL.split(":");
        try
        {
          changelogServers.add(
              InetAddress.getByName(splitStrings[0]).getHostAddress()
              + ":" + splitStrings[1]);
        } catch (UnknownHostException e)
        {
          throw new ConfigException(MSGID_UNKNOWN_HOSTNAME,
              e.getLocalizedMessage());
        }
      }
    }
    configAttributes.add(changelogServer);
    IntegerConfigAttribute windowAttr =
      (IntegerConfigAttribute) config.getConfigAttribute(windowStub);
    if (windowAttr == null)
      rcvWindow = 100;  // Attribute is not present : use the default value
    else
    {
      rcvWindow = windowAttr.activeIntValue();
      configAttributes.add(windowAttr);
    }
    IntegerConfigAttribute queueSizeAttr =
      (IntegerConfigAttribute) config.getConfigAttribute(queueSizeStub);
    if (queueSizeAttr == null)
      queueSize = 10000;  // Attribute is not present : use the default value
    else
    {
      queueSize = queueSizeAttr.activeIntValue();
      configAttributes.add(queueSizeAttr);
    }
    /*
     * read the storage directory path attribute
     */
    StringConfigAttribute dbDirnameAttr =
      (StringConfigAttribute) config.getConfigAttribute(dbDirnameStub);
    if (dbDirnameAttr == null)
    int changelogPort = configuration.getChangelogPort();
    changelogServerId = (short) configuration.getChangelogServerId();
    changelogServers = configuration.getChangelogServer();
    if (changelogServers == null)
      changelogServers = new ArrayList<String>();
    queueSize = configuration.getQueueSize();
    trimAge = configuration.getChangelogPurgeDelay();
    dbDirname = configuration.getChangelogDbDirectory();
    rcvWindow = configuration.getWindowSize();
    if (dbDirname == null)
    {
      dbDirname = "changelogDb";
    }
    else
    {
      dbDirname = dbDirnameAttr.activeValue();
      configAttributes.add(changelogServer);
    }
    // Exists or Create
    // Chech that this path exists or create it.
    File f = getFileForPath(dbDirname);
    try
    {
@@ -326,24 +144,8 @@
          e.getMessage() + " " + getFileForPath(dbDirname));
    }
    /*
     * Read the Purge Delay (trim age) attribute
     */
    IntegerWithUnitConfigAttribute purgeDelayAttr =
      (IntegerWithUnitConfigAttribute) config.getConfigAttribute(
          purgeDelayStub);
    if (purgeDelayAttr == null)
      trimAge = 24*60*60;  // not present : use the default value : 1 day
    else
    {
      trimAge = purgeDelayAttr.activeCalculatedValue();
      configAttributes.add(purgeDelayAttr);
    }
    initialize(changelogServerId, changelogPort);
    configDn = config.getDN();
    DirectoryServer.registerConfigurableComponent(this);
    configuration.addChangeListener(this);
  }
  /**
@@ -452,7 +254,7 @@
         */
        for (String serverURL : changelogServers)
        {
          if ((serverURL.compareTo(localURL) != 0) &&
          if ((serverURL.compareTo(this.serverURL) != 0) &&
              (!connectedChangelogs.contains(serverURL)))
          {
            this.connect(serverURL, changelogCache.getBaseDn());
@@ -629,7 +431,6 @@
    }
    dbEnv.shutdown();
    DirectoryServer.deregisterConfigurableComponent(this);
  }
@@ -659,4 +460,56 @@
  {
    return trimAge * 1000;
  }
  /**
   * Check if the provided configuration is acceptable for add.
   *
   * @param configuration The configuration to check.
   * @param unacceptableReasons When the configuration is not acceptable, this
   *                            table is use to return the reasons why this
   *                            configuration is not acceptbale.
   *
   * @return true if the configuration is acceptable, false other wise.
   */
  public static boolean isConfigurationAcceptable(
      ChangelogServerCfg configuration, List<String> unacceptableReasons)
  {
    int port = configuration.getChangelogPort();
    try
    {
      ServerSocket tmpSocket = new ServerSocket();
      tmpSocket.bind(new InetSocketAddress(port));
      tmpSocket.close();
    }
    catch (Exception e)
    {
      String message = getMessage(MSGID_COULD_NOT_BIND_CHANGELOG, port,
                                  e.getMessage());
      unacceptableReasons.add(message);
      return false;
    }
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
      ChangelogServerCfg configuration)
  {
    // TODO : implement this
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
      ChangelogServerCfg configuration, List<String> unacceptableReasons)
  {
    // TODO : implement this
    return true;
  }
}
opends/src/server/org/opends/server/synchronization/common/LogMessages.java
@@ -48,18 +48,6 @@
       CATEGORY_MASK_SYNC | SEVERITY_MASK_MILD_ERROR | 1;
  /**
   * Need at least one Changelog Server.
   */
  public static final int MSGID_NEED_CHANGELOG_SERVER =
    CATEGORY_MASK_SYNC | SEVERITY_MASK_MILD_ERROR | 2;
  /**
   * Need to have a server ID.
   */
  public static final int MSGID_NEED_SERVER_ID =
    CATEGORY_MASK_SYNC | SEVERITY_MASK_MILD_ERROR | 3;
  /**
   * Invalid Changelog Server.
   */
  public static final int MSGID_INVALID_CHANGELOG_SERVER =
@@ -380,11 +368,7 @@
  public static void registerMessages()
  {
    MessageHandler.registerMessage(MSGID_SYNC_INVALID_DN,
       "The Synchronization configuration DN is invalid");
    MessageHandler.registerMessage(MSGID_NEED_CHANGELOG_SERVER,
        "At least one changelog server must be declared");
    MessageHandler.registerMessage(MSGID_NEED_SERVER_ID,
        "The Server Id must be defined");
       "The configured DN is already used by another domain.");
    MessageHandler.registerMessage(MSGID_INVALID_CHANGELOG_SERVER,
        "Invalid changelog server configuration");
    MessageHandler.registerMessage(MSGID_UNKNOWN_HOSTNAME,
opends/src/server/org/opends/server/synchronization/plugin/ChangelogBroker.java
@@ -33,8 +33,8 @@
import static org.opends.server.synchronization.common.LogMessages.*;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.TreeSet;
import java.util.concurrent.Semaphore;
import java.io.IOException;
@@ -75,7 +75,7 @@
public class ChangelogBroker implements InternalSearchListener
{
  private boolean shutdown = false;
  private List<String> servers;
  private Collection<String> servers;
  private boolean connected = false;
  private final Object lock = new Object();
  private String changelogServer = "Not connected";
@@ -158,7 +158,7 @@
   * @param servers list of servers used
   * @throws Exception : in case of errors
   */
  public void start(List<String> servers)
  public void start(Collection<String> servers)
                    throws Exception
  {
    /*
@@ -504,7 +504,6 @@
      try
      {
        SynchronizationMessage msg = session.receive();
        if (msg instanceof WindowMessage)
        {
          WindowMessage windowMsg = (WindowMessage) msg;
@@ -556,7 +555,8 @@
        debugInfo("ChangelogBroker Stop Closing session");
      }
      session.close();
      if (session != null)
        session.close();
    } catch (IOException e)
    {}
  }
@@ -695,11 +695,31 @@
    return numLostConnections;
  }
  private void log(String message)
  /**
   * Change some config parameters.
   *
   * @param changelogServers    The new list of changelog servers.
   * @param maxReceiveQueue     The max size of receive queue.
   * @param maxReceiveDelay     The max receive delay.
   * @param maxSendQueue        The max send queue.
   * @param maxSendDelay        The max Send Delay.
   * @param window              The max window size.
   * @param heartbeatInterval   The heartbeat interval.
   */
  public void changeConfig(Collection<String> changelogServers,
      int maxReceiveQueue, int maxReceiveDelay, int maxSendQueue,
      int maxSendDelay, int window, long heartbeatInterval)
  {
    int    msgID   = MSGID_UNKNOWN_TYPE;
    logError(ErrorLogCategory.SYNCHRONIZATION,
           ErrorLogSeverity.SEVERE_ERROR,
           message, msgID);
    this.servers = changelogServers;
    this.maxRcvWindow = window;
    this.heartbeatInterval = heartbeatInterval;
    this.maxReceiveDelay = maxReceiveDelay;
    this.maxReceiveQueue = maxReceiveQueue;
    this.maxSendDelay = maxSendDelay;
    this.maxSendQueue = maxSendQueue;
    // TODO : Changing those parameters requires to either restart a new
    // session with the changelog server or renegociate the parameters that
    // were sent in the ServerStart message
  }
}
opends/src/server/org/opends/server/synchronization/plugin/ChangelogListener.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.synchronization.plugin;
import java.util.List;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.std.server.ChangelogServerCfg;
import org.opends.server.admin.std.server.MultimasterSynchronizationProviderCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.synchronization.changelog.Changelog;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.ResultCode;
/**
 * This class is used to create and object that can
 * register in the admin framework as a listener for changes, add and delete
 * on the Changelog Server configuration objects.
 *
 */
public class ChangelogListener
       implements ConfigurationAddListener<ChangelogServerCfg>,
       ConfigurationDeleteListener<ChangelogServerCfg>
{
  Changelog changelog = null;
  /**
   * Build a Changelog Listener from the given Multimaster configuration.
   *
   * @param configuration The configuration that will be used to listen
   *                      for changelog configuration changes.
   *
   * @throws ConfigException if the ChangelogListener can't register for
   *                         listening to changes on the provided configuration
   *                         object.
   */
  public ChangelogListener(
      MultimasterSynchronizationProviderCfg configuration)
      throws ConfigException
  {
    configuration.addChangelogServerAddListener(this);
    configuration.addChangelogServerDeleteListener(this);
    if (configuration.hasChangelogServer())
    {
      ChangelogServerCfg server = configuration.getChangelogServer();
      changelog = new Changelog(server);
    }
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(
      ChangelogServerCfg configuration)
  {
    try
    {
      changelog = new Changelog(configuration);
      return new ConfigChangeResult(ResultCode.SUCCESS, false);
    } catch (ConfigException e)
    {
      // we should never get to this point because the configEntry has
      // already been validated in configAddisAcceptable
      return new ConfigChangeResult(ResultCode.CONSTRAINT_VIOLATION, false);
    }
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationAddAcceptable(
      ChangelogServerCfg configuration, List<String> unacceptableReasons)
  {
    return Changelog.isConfigurationAcceptable(
      configuration, unacceptableReasons);
  }
  /**
   * Shutdown the Changelog servers.
   */
  public void shutdown()
  {
    if (changelog != null)
      changelog.shutdown();
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationDelete(
      ChangelogServerCfg configuration)
  {
    // There can be only one changelog, just shutdown the changelog
    // currently configured.
    if (changelog != null)
    {
      changelog.shutdown();
    }
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationDeleteAcceptable(
      ChangelogServerCfg configuration, List<String> unacceptableReasons)
  {
    return true;
  }
}
opends/src/server/org/opends/server/synchronization/plugin/MultimasterSynchronization.java
@@ -26,34 +26,35 @@
 */
package org.opends.server.synchronization.plugin;
import static org.opends.server.synchronization.common.LogMessages.HISTORICAL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.std.server.MultimasterDomainCfg;
import org.opends.server.admin.std.server.MultimasterSynchronizationProviderCfg;
import org.opends.server.api.Backend;
import org.opends.server.api.BackupTaskListener;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.api.ConfigChangeListener;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.api.ExportTaskListener;
import org.opends.server.api.ImportTaskListener;
import org.opends.server.api.RestoreTaskListener;
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AddOperation;
import org.opends.server.synchronization.changelog.Changelog;
import org.opends.server.synchronization.common.LogMessages;
import org.opends.server.types.DN;
import org.opends.server.core.DeleteOperation;
import org.opends.server.types.DirectoryException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.Entry;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.Operation;
import org.opends.server.synchronization.common.LogMessages;
import org.opends.server.types.BackupConfig;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.Modification;
@@ -61,8 +62,6 @@
import org.opends.server.types.ResultCode;
import org.opends.server.types.SynchronizationProviderResult;
import static org.opends.server.synchronization.common.LogMessages.*;
/**
 * This class is used to load the Synchronization code inside the JVM
 * and to trigger initialization of the synchronization.
@@ -71,19 +70,19 @@
 * synchronization code running during the operation process
 * as pre-op, conflictRsolution, and post-op.
 */
public class MultimasterSynchronization extends SynchronizationProvider
       implements ConfigAddListener, ConfigDeleteListener, ConfigChangeListener,
       BackupTaskListener, RestoreTaskListener, ImportTaskListener,
       ExportTaskListener
public class MultimasterSynchronization
       extends SynchronizationProvider<MultimasterSynchronizationProviderCfg>
       implements ConfigurationAddListener<MultimasterDomainCfg>,
                  ConfigurationDeleteListener<MultimasterDomainCfg>,
                  BackupTaskListener, RestoreTaskListener, ImportTaskListener,
                  ExportTaskListener
{
  static String CHANGELOG_DN = "cn=Changelog Server," +
    "cn=Multimaster Synchronization, cn=Synchronization Providers, cn=config";
  static String SYNCHRONIZATION_CLASS =
    "ds-cfg-synchronization-provider-config";
  private DN changelogConfigEntryDn = null;
  private Changelog changelog = null;
  private ChangelogListener changelog = null;
  private static Map<DN, SynchronizationDomain> domains =
    new HashMap<DN, SynchronizationDomain>() ;
@@ -91,46 +90,25 @@
  /**
   * {@inheritDoc}
   */
  public void initializeSynchronizationProvider(ConfigEntry configEntry)
  @Override
  public void initializeSynchronizationProvider(
      MultimasterSynchronizationProviderCfg configuration)
  throws ConfigException
  {
    LogMessages.registerMessages();
    configEntry.registerAddListener(this);
    configEntry.registerDeleteListener(this);
    changelog = new ChangelogListener(configuration);
    /*
     * Read changelog server the changelog configuration entry
     */
    try
    {
      changelogConfigEntryDn = DN.decode(CHANGELOG_DN);
      ConfigEntry config =
        DirectoryServer.getConfigEntry(changelogConfigEntryDn);
      /*
       * If there is no such entry, this process must not be a changelog server
       */
      if (config != null)
      {
        changelog = new Changelog(config);
      }
    } catch (DirectoryException e)
    {
      /* never happens */
      throw new ConfigException(MSGID_SYNC_INVALID_DN,
      "Invalid Changelog configuration DN");
    }
    // Register as an add and delete listener with the root configuration so we
    // can be notified if Multimaster domain entries are added or removed.
    configuration.addMultimasterDomainAddListener(this);
    configuration.addMultimasterDomainDeleteListener(this);
    /*
     * Parse the list of entries below configEntry,
     * create one synchronization domain for each child
     */
    for (ConfigEntry domainEntry : configEntry.getChildren().values())
    //  Create the list of domains that are already defined.
    for (String name : configuration.listMultimasterDomains())
    {
      if (domainEntry.hasObjectClass(SYNCHRONIZATION_CLASS))
      {
        createNewSynchronizationDomain(domainEntry);
      }
      MultimasterDomainCfg domain = configuration.getMultimasterDomain(name);
      createNewSynchronizationDomain(domain);
    }
    /*
@@ -151,124 +129,46 @@
  }
  /**
   * Indicates whether the configuration entry that will result from a proposed
   * modification is acceptable to this change listener.
   *
   * @param  configEntry         The configuration entry that will result from
   *                             the requested update.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed change is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry contains an acceptable
   *          configuration, or <CODE>false</CODE> if it does not.
   * {@inheritDoc}
   */
  public boolean configChangeIsAcceptable(ConfigEntry configEntry,
                                          StringBuilder unacceptableReason)
  public boolean isConfigurationAddAcceptable(
      MultimasterDomainCfg configuration, List<String> unacceptableReasons)
  {
    return false; // TODO :NYI
  }
  /**
   * Attempts to apply a new configuration to this Directory Server component
   * based on the provided changed entry.
   *
   * @param  configEntry  The configuration entry that containing the updated
   *                      configuration for this component.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   */
  public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry)
  {
    // TODO implement this method
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
    return SynchronizationDomain.isConfigurationAcceptable(
      configuration, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  public boolean configAddIsAcceptable(ConfigEntry configEntry,
      StringBuilder unacceptableReason)
  public ConfigChangeResult applyConfigurationAdd(
     MultimasterDomainCfg configuration)
  {
    // Check if the added entry is the changelog config entry
    try
    {
      if (configEntry.getDN().equals(DN.decode(CHANGELOG_DN)))
      {
        return Changelog.checkConfigEntry(configEntry, unacceptableReason);
      }
    } catch (DirectoryException e)
      createNewSynchronizationDomain(configuration);
      return new ConfigChangeResult(ResultCode.SUCCESS, false);
    } catch (ConfigException e)
    {
      /* never happens */
       unacceptableReason.append("Invalid Changelog configuration DN");
       return false;
      // we should never get to this point because the configEntry has
      // already been validated in configAddisAcceptable
      return new ConfigChangeResult(ResultCode.CONSTRAINT_VIOLATION, false);
    }
    // otherwise it must be a Synchronization domain, check for
    // presence of the Synchronization configuration object class
    if (configEntry.hasObjectClass(SYNCHRONIZATION_CLASS))
    {
      return SynchronizationDomain.checkConfigEntry(configEntry,
          unacceptableReason);
    }
    return false;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry)
  {
    // check if the entry is the changelog configuration entry
    if (configEntry.getDN().equals(changelogConfigEntryDn))
    {
      try
      {
        changelog = new Changelog(configEntry);
        return new ConfigChangeResult(ResultCode.SUCCESS, false);
      } catch (ConfigException e)
      {
        // we should never get to this point because the configEntry has
        // already been validated in configAddisAcceptable
        return new ConfigChangeResult(ResultCode.SUCCESS, false);
      }
    }
    // otherwise it must be a synchronization domain, check for
    // presence of the Synchronization configuration object class
    if (configEntry.hasObjectClass(SYNCHRONIZATION_CLASS))
    {
      try
      {
        createNewSynchronizationDomain(configEntry);
        return new ConfigChangeResult(ResultCode.SUCCESS, false);
      } catch (ConfigException e)
      {
        // we should never get to this point because the configEntry has
        // already been validated in configAddisAcceptable
        return new ConfigChangeResult(ResultCode.SUCCESS, false);
      }
    }
    // we should never get to this point because the configEntry has
    // already been validated in configAddisAcceptable
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
  }
  /**
   * Creates a New Synchronization domain from its configEntry, do the
   * necessary initialization and starts it so that it is
   * fully operational when this method returns.
   * @param configEntry The entry whith the configuration of this domain.
   * @param configuration The entry whith the configuration of this domain.
   * @throws ConfigException When the configuration is not valid.
   */
  private void createNewSynchronizationDomain(ConfigEntry configEntry)
          throws ConfigException
  private void createNewSynchronizationDomain(
      MultimasterDomainCfg configuration)
      throws ConfigException
  {
    SynchronizationDomain domain;
    domain = new SynchronizationDomain(configEntry);
    domain = new SynchronizationDomain(configuration);
    domains.put(domain.getBaseDN(), domain);
    domain.start();
  }
@@ -276,25 +176,7 @@
  /**
   * {@inheritDoc}
   */
  public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
      StringBuilder unacceptableReason)
  {
    // TODO Auto-generated method stub
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry)
  {
    // TODO Auto-generated method stub
    return null;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void doPostOperation(AddOperation addOperation)
  {
    DN dn = addOperation.getEntryDN();
@@ -305,6 +187,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public void doPostOperation(DeleteOperation deleteOperation)
  {
    DN dn = deleteOperation.getEntryDN();
@@ -314,6 +197,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public void doPostOperation(ModifyDNOperation modifyDNOperation)
  {
    DN dn = modifyDNOperation.getEntryDN();
@@ -536,6 +420,7 @@
   *                                      applied to the schema.
   *
   */
  @Override
  public void processSchemaChange(List<Modification> modifications)
  {
    SynchronizationDomain domain =
@@ -651,6 +536,29 @@
        domain.backupEnd();
    }
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationDelete(
      MultimasterDomainCfg configuration)
  {
    DN dn = configuration.getSynchronizationDN();
    SynchronizationDomain domain = domains.remove(dn);
    if (domain != null)
      domain.shutdown();
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationDeleteAcceptable(
      MultimasterDomainCfg configuration, List<String> unacceptableReasons)
  {
    return true;
  }
}
opends/src/server/org/opends/server/synchronization/plugin/SynchronizationDomain.java
@@ -39,14 +39,13 @@
import static org.opends.server.synchronization.common.LogMessages.*;
import static org.opends.server.synchronization.plugin.Historical.ENTRYUIDNAME;
import static org.opends.server.synchronization.protocol.OperationContext.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.createEntry;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
@@ -55,20 +54,18 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.DataFormatException;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.MultimasterDomainCfg;
import org.opends.server.api.Backend;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.api.DirectoryThread;
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.backends.jeb.BackendImpl;
import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskState;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.config.IntegerConfigAttribute;
import org.opends.server.config.IntegerWithUnitConfigAttribute;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
@@ -77,7 +74,6 @@
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.Operation;
import org.opends.server.messages.MessageHandler;
import org.opends.server.protocols.asn1.ASN1Exception;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
@@ -130,7 +126,7 @@
 *  handle protocol messages from the changelog server.
 */
public class SynchronizationDomain extends DirectoryThread
       implements ConfigurableComponent
       implements ConfigurationChangeListener<MultimasterDomainCfg>
{
  private SynchronizationMonitor monitor;
@@ -254,7 +250,7 @@
  private int listenerThreadNumber = 10;
  private boolean receiveStatus = true;
  private List<String> changelogServers;
  private Collection<String> changelogServers;
  private DN baseDN;
@@ -263,8 +259,6 @@
  private boolean shutdown = false;
  private DN configDn;
  private InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
@@ -273,102 +267,30 @@
  private boolean disabled = false;
  private boolean stateSavingDisabled = false;
  static final String CHANGELOG_SERVER_ATTR = "ds-cfg-changelog-server";
  static final String BASE_DN_ATTR = "ds-cfg-synchronization-dn";
  static final String SERVER_ID_ATTR = "ds-cfg-directory-server-id";
  static final String RECEIVE_STATUS = "ds-cfg-receive-status";
  static final String MAX_RECEIVE_QUEUE = "ds-cfg-max-receive-queue";
  static final String MAX_RECEIVE_DELAY = "ds-cfg-max-receive-delay";
  static final String MAX_SEND_QUEUE = "ds-cfg-max-send-queue";
  static final String MAX_SEND_DELAY = "ds-cfg-max-send-delay";
  static final String WINDOW_SIZE = "ds-cfg-window-size";
  static final String HEARTBEAT_INTERVAL = "ds-cfg-heartbeat-interval";
  private int window = 100;
  private static final StringConfigAttribute changelogStub =
    new StringConfigAttribute(CHANGELOG_SERVER_ATTR,
        "changelog server information", true, true, false);
  private static final IntegerConfigAttribute serverIdStub =
    new IntegerConfigAttribute(SERVER_ID_ATTR, "server ID", true, false,
                               false, true, 0, true, 65535);
  private static final DNConfigAttribute baseDnStub =
    new DNConfigAttribute(BASE_DN_ATTR, "synchronization base DN",
                          true, false, false);
  private static final BooleanConfigAttribute receiveStatusStub =
    new BooleanConfigAttribute(RECEIVE_STATUS, "receive status", false);
  /**
   * The set of time units that will be used for expressing the heartbeat
   * interval.
   */
  private static final LinkedHashMap<String,Double> timeUnits =
       new LinkedHashMap<String,Double>();
  static
  {
    timeUnits.put(TIME_UNIT_MILLISECONDS_ABBR, 1D);
    timeUnits.put(TIME_UNIT_MILLISECONDS_FULL, 1D);
    timeUnits.put(TIME_UNIT_SECONDS_ABBR, 1000D);
    timeUnits.put(TIME_UNIT_SECONDS_FULL, 1000D);
  }
  /**
   * Creates a new SynchronizationDomain using configuration from configEntry.
   *
   * @param configEntry The ConfigEntry to use to read the configuration of this
   *                    SynchronizationDomain.
   * @param configuration    The configuration of this SynchronizationDomain.
   * @throws ConfigException In case of invalid configuration.
   */
  public SynchronizationDomain(ConfigEntry configEntry) throws ConfigException
  public SynchronizationDomain(MultimasterDomainCfg configuration)
    throws ConfigException
  {
    super("Synchronization flush");
    /*
     * read the centralized changelog server configuration
     * this is a multivalued attribute
     */
    StringConfigAttribute changelogServer =
      (StringConfigAttribute) configEntry.getConfigAttribute(changelogStub);
    if (changelogServer == null)
    {
      throw new ConfigException(MSGID_NEED_CHANGELOG_SERVER,
          MessageHandler.getMessage(MSGID_NEED_CHANGELOG_SERVER,
              configEntry.getDN().toString()) );
    }
    changelogServers = changelogServer.activeValues();
    configAttributes.add(changelogServer);
    /*
     * read the server Id information
     * this is a single valued integer, its value must fit on a short integer
     */
    IntegerConfigAttribute serverIdAttr =
      (IntegerConfigAttribute) configEntry.getConfigAttribute(serverIdStub);
    if (serverIdAttr == null)
    {
      throw new ConfigException(MSGID_NEED_SERVER_ID,
          MessageHandler.getMessage(MSGID_NEED_SERVER_ID,
              configEntry.getDN().toString())  );
    }
    serverId = (short) serverIdAttr.activeIntValue();
    configAttributes.add(serverIdAttr);
    /*
     * read the base DN
     */
    DNConfigAttribute baseDn =
      (DNConfigAttribute) configEntry.getConfigAttribute(baseDnStub);
    if (baseDn == null)
      baseDN = null;  // Attribute is not present : don't set a limit
    else
      baseDN = baseDn.activeValue();
    configAttributes.add(baseDn);
    // Read the configuration parameters.
    changelogServers = configuration.getChangelogServer();
    serverId = (short) configuration.getServerId();
    baseDN = configuration.getSynchronizationDN();
    maxReceiveQueue = configuration.getMaxReceiveQueue();
    maxReceiveDelay = (int) configuration.getMaxReceiveDelay();
    maxSendQueue = configuration.getMaxSendQueue();
    maxSendDelay = (int) configuration.getMaxSendDelay();
    window  = configuration.getWindowSize();
    heartbeatInterval = configuration.getHeartbeatInterval();
    /*
     * Modify conflicts are solved for all suffixes but the schema suffix
@@ -386,114 +308,25 @@
      solveConflictFlag = true;
    }
    /*
     * Create a new Persistent Server State that will be used to store
     * the last ChangeNmber seen from all LDAP servers in the topology.
     */
    state = new PersistentServerState(baseDN);
    /*
     * Read the Receive Status.
     * Create a Synchronization monitor object responsible for publishing
     * monitoring information below cn=monitor.
     */
    BooleanConfigAttribute receiveStatusAttr = (BooleanConfigAttribute)
          configEntry.getConfigAttribute(receiveStatusStub);
    if (receiveStatusAttr != null)
    {
      receiveStatus = receiveStatusAttr.activeValue();
      configAttributes.add(receiveStatusAttr);
    }
    /*
     * read the synchronization flow control configuration.
     */
    IntegerConfigAttribute maxReceiveQueueStub =
      new IntegerConfigAttribute(MAX_RECEIVE_QUEUE, "max receive queue",
                                 false, false, false, true, 0,false, 0);
    IntegerConfigAttribute maxReceiveQueueAttr = (IntegerConfigAttribute)
              configEntry.getConfigAttribute(maxReceiveQueueStub);
    if (maxReceiveQueueAttr == null)
      maxReceiveQueue = 0;  // Attribute is not present : don't set a limit
    else
    {
      maxReceiveQueue = maxReceiveQueueAttr.activeIntValue();
      configAttributes.add(maxReceiveQueueAttr);
    }
    IntegerConfigAttribute maxReceiveDelayStub =
      new IntegerConfigAttribute(MAX_RECEIVE_DELAY, "max receive delay",
                                 false, false, false, true, 0, false, 0);
    IntegerConfigAttribute maxReceiveDelayAttr = (IntegerConfigAttribute)
              configEntry.getConfigAttribute(maxReceiveDelayStub);
    if (maxReceiveDelayAttr == null)
      maxReceiveDelay = 0;  // Attribute is not present : don't set a limit
    else
    {
      maxReceiveDelay = maxReceiveDelayAttr.activeIntValue();
      configAttributes.add(maxReceiveDelayAttr);
    }
    IntegerConfigAttribute maxSendQueueStub =
      new IntegerConfigAttribute(MAX_SEND_QUEUE, "max send queue",
                                 false, false, false, true, 0, false, 0);
    IntegerConfigAttribute maxSendQueueAttr =
      (IntegerConfigAttribute) configEntry.getConfigAttribute(maxSendQueueStub);
    if (maxSendQueueAttr == null)
      maxSendQueue = 0;  // Attribute is not present : don't set a limit
    else
    {
      maxSendQueue = maxSendQueueAttr.activeIntValue();
      configAttributes.add(maxSendQueueAttr);
    }
    IntegerConfigAttribute maxSendDelayStub =
      new IntegerConfigAttribute(MAX_SEND_DELAY, "max send delay",
                                 false, false, false, true, 0, false, 0);
    IntegerConfigAttribute maxSendDelayAttr =
      (IntegerConfigAttribute) configEntry.getConfigAttribute(maxSendDelayStub);
    if (maxSendDelayAttr == null)
      maxSendDelay = 0;  // Attribute is not present : don't set a limit
    else
    {
      maxSendDelay = maxSendDelayAttr.activeIntValue();
      configAttributes.add(maxSendDelayAttr);
    }
    Integer window;
    IntegerConfigAttribute windowStub =
      new IntegerConfigAttribute(WINDOW_SIZE, "window size",
                                 false, false, false, true, 0, false, 0);
    IntegerConfigAttribute windowAttr =
      (IntegerConfigAttribute) configEntry.getConfigAttribute(windowStub);
    if (windowAttr == null)
      window = 100;  // Attribute is not present : use the default value
    else
    {
      window = windowAttr.activeIntValue();
      configAttributes.add(windowAttr);
    }
    IntegerWithUnitConfigAttribute heartbeatStub =
      new IntegerWithUnitConfigAttribute(HEARTBEAT_INTERVAL,
                                         "heartbeat interval",
                                         false, timeUnits, true, 0, false, 0);
    IntegerWithUnitConfigAttribute heartbeatAttr =
      (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(heartbeatStub);
    if (heartbeatAttr == null)
    {
      // Attribute is not present : use the default value
      heartbeatInterval = 1000;
    }
    else
    {
      heartbeatInterval = heartbeatAttr.activeCalculatedValue();
      configAttributes.add(heartbeatAttr);
    }
    configDn = configEntry.getDN();
    DirectoryServer.registerConfigurableComponent(this);
    monitor = new SynchronizationMonitor(this);
    DirectoryServer.registerMonitorProvider(monitor);
    /*
     * ChangeNumberGenerator is used to create new unique ChangeNumbers
     * for each operation done on the synchronization domain.
     */
    changeNumberGenerator = new ChangeNumberGenerator(serverId, state);
    /*
     * create the broker object used to publish and receive changes
     */
@@ -519,14 +352,9 @@
      * should we stop the modifications ?
      */
    }
  }
  /**
   * {@inheritDoc}
   */
  public DN getConfigurableComponentEntryDN()
  {
    return configDn;
    // listen for changes on the configuration
    configuration.addChangeListener(this);
  }
  /**
@@ -537,112 +365,6 @@
    return configAttributes;
  }
  /**
   * {@inheritDoc}
   */
  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
      List<String> unacceptableReasons)
  {
    boolean acceptable = true;
    StringConfigAttribute changelog = null;
    try
    {
      changelog = (StringConfigAttribute)
                                  configEntry.getConfigAttribute(changelogStub);
    } catch (ConfigException e)
    {
      acceptable = false;
      unacceptableReasons.add("Need at least one changelog server.");
    }
    if (changelog == null)
    {
      acceptable = false;
      unacceptableReasons.add("Need at least one changelog server.");
    }
    return acceptable;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
      boolean detailedResults)
  {
    StringConfigAttribute changelog = null;
    List<String> newChangelogServers;
    boolean newReceiveStatus;
    try
    {
      /*
       *  check if changelog server list changed
       */
      changelog = (StringConfigAttribute)
                                  configEntry.getConfigAttribute(changelogStub);
      newChangelogServers = changelog.activeValues();
      boolean sameConf = true;
      for (String s :newChangelogServers)
        if (!changelogServers.contains(s))
          sameConf = false;
      for (String s : changelogServers)
        if (!newChangelogServers.contains(s))
          sameConf = false;
      if (!sameConf)
      {
        broker.stop();
        changelogServers = newChangelogServers;
        broker.start(changelogServers);
      }
      /*
       * check if reception should be disabled
       */
      newReceiveStatus = ((BooleanConfigAttribute)
               configEntry.getConfigAttribute(receiveStatusStub)).activeValue();
      if (newReceiveStatus != receiveStatus)
      {
        /*
         * was disabled and moved to enabled
         */
        if (newReceiveStatus)
        {
          broker.restartReceive();
          for (int i=0; i<listenerThreadNumber; i++)
          {
            ListenerThread myThread = new ListenerThread(this);
            myThread.start();
            synchroThreads.add(myThread);
          }
        }
        else
        {
          /* was enabled and moved to disabled */
          broker.suspendReceive();
          // FIXME Need a way to stop these threads.
          // Setting the shutdown flag does not stop them until they have
          // consumed and discarded one more message each.
//          for (ListenerThread thread : synchroThreads)
//          {
//            thread.shutdown();
//          }
          synchroThreads.clear();
        }
        receiveStatus = newReceiveStatus;
      }
    } catch (Exception e)
    {
      /* this should never happen because the parameters have been
       * validated by hasAcceptableConfiguration
       */
      return new ConfigChangeResult(ResultCode.OPERATIONS_ERROR, false);
    }
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
  }
  /**
   * Returns the base DN of this SynchronizationDomain.
@@ -1041,9 +763,7 @@
  public void receiveAck(AckMessage ack)
  {
    UpdateMessage update;
    ChangeNumber changeNumber;
    changeNumber = ack.getChangeNumber();
    ChangeNumber changeNumber = ack.getChangeNumber();
    synchronized (pendingChanges)
    {
@@ -1933,51 +1653,6 @@
  }
  /**
   * Check if a ConfigEntry is valid.
   * @param configEntry The config entry that needs to be checked.
   * @param unacceptableReason A description of the reason why the config entry
   *                           is not acceptable (if return is false).
   * @return a boolean indicating if the configEntry is valid.
   */
  public static boolean checkConfigEntry(ConfigEntry configEntry,
      StringBuilder unacceptableReason)
  {
    try
    {
    StringConfigAttribute changelogServer =
      (StringConfigAttribute) configEntry.getConfigAttribute(changelogStub);
    if (changelogServer == null)
    {
      unacceptableReason.append(
          MessageHandler.getMessage(MSGID_NEED_CHANGELOG_SERVER,
          configEntry.getDN().toString()) );
      return false;
    }
    /*
     * read the server Id information
     * this is a single valued integer, its value must fit on a short integer
     */
    IntegerConfigAttribute serverIdAttr =
      (IntegerConfigAttribute) configEntry.getConfigAttribute(serverIdStub);
    if (serverIdAttr == null)
    {
      unacceptableReason.append(
          MessageHandler.getMessage(MSGID_NEED_SERVER_ID,
              configEntry.getDN().toString()) );
      return false;
    }
    }
    catch (ConfigException e)
    {
      unacceptableReason.append(e.getMessage());
      return false;
    }
    return true;
  }
  /**
   * Get the maximum receive window size.
   *
   * @return The maximum receive window size.
@@ -2026,7 +1701,6 @@
    return broker.getNumLostConnections();
  }
  /**
   * Check if the domain solve conflicts.
   *
@@ -3109,4 +2783,60 @@
    op.setResultCode(ResultCode.SUCCESS);
    synchronize(op);
  }
  /**
   * Check if the provided configuration is acceptable for add.
   *
   * @param configuration The configuration to check.
   * @param unacceptableReasons When the configuration is not acceptable, this
   *                            table is use to return the reasons why this
   *                            configuration is not acceptbale.
   *
   * @return true if the configuration is acceptable, false other wise.
   */
  public static boolean isConfigurationAcceptable(
      MultimasterDomainCfg configuration, List<String> unacceptableReasons)
  {
    // Check that there is not already a domain with the same DN
    // TODO : Check that the server id is a short
    DN dn = configuration.getSynchronizationDN();
    if (MultimasterSynchronization.findDomain(dn,null) != null)
    {
      String message = getMessage(MSGID_SYNC_INVALID_DN, dn.toString());
      unacceptableReasons.add(message);
      return false;
    }
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
         MultimasterDomainCfg configuration)
  {
    // server id and base dn are readonly.
    // The other parameters needs to be renegociated with the Changelog Server.
    // so that requires restarting the session with the Changelog Server.
    changelogServers = configuration.getChangelogServer();
    maxReceiveQueue = configuration.getMaxReceiveQueue();
    maxReceiveDelay = (int) configuration.getMaxReceiveDelay();
    maxSendQueue = configuration.getMaxSendQueue();
    maxSendDelay = (int) configuration.getMaxSendDelay();
    window = configuration.getWindowSize();
    heartbeatInterval = configuration.getHeartbeatInterval();
    broker.changeConfig(changelogServers, maxReceiveQueue, maxReceiveDelay,
                        maxSendQueue, maxSendDelay, window, heartbeatInterval);
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
         MultimasterDomainCfg configuration, List<String> unacceptableReasons)
  {
    return true;
  }
}
opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java
@@ -35,6 +35,7 @@
import java.util.TreeSet;
import java.util.concurrent.locks.Lock;
import org.opends.server.admin.std.server.SynchronizationProviderCfg;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.backends.task.Task;
@@ -298,7 +299,7 @@
      if (! mods.isEmpty())
      {
        for (SynchronizationProvider provider :
        for (SynchronizationProvider<SynchronizationProviderCfg> provider :
             DirectoryServer.getSynchronizationProviders())
        {
          try
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/InitOnLineTest.java
@@ -26,11 +26,7 @@
 */
package org.opends.server.synchronization;
import static org.opends.server.config.ConfigConstants.ATTR_TASK_COMPLETION_TIME;
import static org.opends.server.config.ConfigConstants.ATTR_TASK_INITIALIZE_DONE;
import static org.opends.server.config.ConfigConstants.ATTR_TASK_INITIALIZE_LEFT;
import static org.opends.server.config.ConfigConstants.ATTR_TASK_LOG_MESSAGES;
import static org.opends.server.config.ConfigConstants.ATTR_TASK_STATE;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.Error.logError;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.debug.DebugLogger.debugInfo;
@@ -40,49 +36,35 @@
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.UUID;
import org.opends.server.TestCaseUtils;
import org.opends.server.backends.task.TaskState;
import org.opends.server.config.ConfigEntry;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.messages.TaskMessages;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.schema.DirectoryStringSyntax;
import org.opends.server.synchronization.changelog.Changelog;
import org.opends.server.synchronization.changelog.ChangelogFakeConfiguration;
import org.opends.server.synchronization.common.LogMessages;
import org.opends.server.synchronization.common.ServerState;
import org.opends.server.synchronization.plugin.ChangelogBroker;
import org.opends.server.synchronization.plugin.SynchronizationDomain;
import org.opends.server.synchronization.protocol.ChangelogStartMessage;
import org.opends.server.synchronization.protocol.DoneMessage;
import org.opends.server.synchronization.protocol.EntryMessage;
import org.opends.server.synchronization.protocol.ErrorMessage;
import org.opends.server.synchronization.protocol.InitializeRequestMessage;
import org.opends.server.synchronization.protocol.InitializeTargetMessage;
import org.opends.server.synchronization.protocol.RoutableMessage;
import org.opends.server.synchronization.protocol.ServerStartMessage;
import org.opends.server.synchronization.protocol.SocketSession;
import org.opends.server.synchronization.protocol.SynchronizationMessage;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.Modification;
import org.opends.server.types.ModificationType;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
@@ -193,14 +175,6 @@
    // Synchro multi-master
    synchroPluginStringDN = "cn=Multimaster Synchronization, "
      + synchroStringDN;
    String synchroPluginLdif = "dn: "
      + synchroPluginStringDN
      + "\n"
      + "objectClass: top\n"
      + "objectClass: ds-cfg-synchronization-provider\n"
      + "ds-cfg-synchronization-provider-enabled: true\n"
      + "ds-cfg-synchronization-provider-class: org.opends.server.synchronization.MultimasterSynchronization\n";
    synchroPluginEntry = TestCaseUtils.entryFromLdifString(synchroPluginLdif);
    // Synchro suffix
    synchroServerEntry = null;
@@ -762,26 +736,10 @@
      {
        int chPort = getChangelogPort(changelogId);
        
        // Create a changelog server
        String changelogLdif = "dn: cn=Changelog Server\n"
          + "objectClass: top\n"
          + "objectClass: ds-cfg-synchronization-changelog-server-config\n"
          + "cn: Changelog Server\n"
          + "ds-cfg-changelog-port: " + chPort + "\n"
          + "ds-cfg-changelog-server-id: " + changelogId + "\n"
//        + "ds-cfg-heartbeat-interval: 0 ms\n"
          + "ds-cfg-window-size: 100" + "\n";
        if (changelogId==changelog2ID)
        {
          changelogLdif += new String(
              "ds-cfg-changelog-server: localhost:"
              + getChangelogPort(changelog1ID)+"\n");
        }
        Entry tmp = TestCaseUtils.entryFromLdifString(changelogLdif);
        ConfigEntry changelogConfig = new ConfigEntry(tmp, null);
        Changelog changelog = new Changelog(changelogConfig);
        ChangelogFakeConfiguration conf =
          new ChangelogFakeConfiguration(chPort, null, 0, changelogId, 0, 100,
                                         null);
        Changelog changelog = new Changelog(conf);
        Thread.sleep(1000);
        return changelog;
@@ -806,7 +764,8 @@
    {
      // suffix synchronized
      String synchroServerStringDN = synchroPluginStringDN;
      String synchroServerLdif = "dn: cn=example," + synchroServerStringDN + "\n"
      String synchroServerLdif =
        "dn: cn=example, cn=domains" + synchroServerStringDN + "\n"
      + "objectClass: top\n"
      + "objectClass: ds-cfg-synchronization-provider-config\n"
      + "cn: example\n"
@@ -1483,6 +1442,6 @@
      // fromthe changelog server.
      server2 = null;
    }
    super.cleanEntries();
    super.cleanRealEntries();
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ProtocolWindowTest.java
@@ -273,14 +273,6 @@
    // Multimaster Synchro plugin
    synchroPluginStringDN = "cn=Multimaster Synchronization, "
        + synchroStringDN;
    String synchroPluginLdif = "dn: "
        + synchroPluginStringDN
        + "\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-provider\n"
        + "ds-cfg-synchronization-provider-enabled: true\n"
        + "ds-cfg-synchronization-provider-class: org.opends.server.synchronization.MultimasterSynchronization\n";
    synchroPluginEntry = TestCaseUtils.entryFromLdifString(synchroPluginLdif);
    // Change log
    String changeLogStringDN = "cn=Changelog Server, " + synchroPluginStringDN;
@@ -294,7 +286,8 @@
    changeLogEntry = TestCaseUtils.entryFromLdifString(changeLogLdif);
    // suffix synchronized
    String synchroServerStringDN = "cn=example, " + synchroPluginStringDN;
    String synchroServerStringDN =
      "cn=example, cn=domains, " + synchroPluginStringDN;
    String synchroServerLdif = "dn: " + synchroServerStringDN + "\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-provider-config\n"
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ReSyncTest.java
@@ -89,14 +89,6 @@
    // Multimaster Synchro plugin
    synchroPluginStringDN = "cn=Multimaster Synchronization, "
        + synchroStringDN;
    String synchroPluginLdif = "dn: "
        + synchroPluginStringDN
        + "\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-provider\n"
        + "ds-cfg-synchronization-provider-enabled: true\n"
        + "ds-cfg-synchronization-provider-class: org.opends.server.synchronization.MultimasterSynchronization\n";
    synchroPluginEntry = TestCaseUtils.entryFromLdifString(synchroPluginLdif);
    // Change log
    String changeLogStringDN = "cn=Changelog Server, " + synchroPluginStringDN;
@@ -109,7 +101,8 @@
    changeLogEntry = TestCaseUtils.entryFromLdifString(changeLogLdif);
    // suffix synchronized
    String synchroServerLdif = "dn: cn=example, " + synchroPluginStringDN + "\n"
    String synchroServerLdif =
      "dn: cn=example, cn=domains, " + synchroPluginStringDN + "\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-provider-config\n"
        + "cn: example\n"
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SchemaSynchronizationTest.java
@@ -38,6 +38,7 @@
import java.util.List;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.server.SynchronizationProviderCfg;
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
@@ -98,14 +99,6 @@
    // Multimaster Synchro plugin
    synchroPluginStringDN = "cn=Multimaster Synchronization, "
        + synchroStringDN;
    String synchroPluginLdif = "dn: "
        + synchroPluginStringDN
        + "\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-provider\n"
        + "ds-cfg-synchronization-provider-enabled: true\n"
        + "ds-cfg-synchronization-provider-class: org.opends.server.synchronization.MultimasterSynchronization\n";
    synchroPluginEntry = TestCaseUtils.entryFromLdifString(synchroPluginLdif);
    // Change log
    String changeLogStringDN = "cn=Changelog Server, " + synchroPluginStringDN;
@@ -118,7 +111,8 @@
    changeLogEntry = TestCaseUtils.entryFromLdifString(changeLogLdif);
    // suffix synchronized
    String synchroServerLdif = "dn: cn=example, " + synchroPluginStringDN + "\n"
    String synchroServerLdif =
      "dn: cn=example, cn=domains, " + synchroPluginStringDN + "\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-provider-config\n"
        + "cn: example\n"
@@ -276,7 +270,7 @@
    Modification mod = new Modification(ModificationType.ADD, attr);
    mods.add(mod);
    for (SynchronizationProvider provider :
    for (SynchronizationProvider<SynchronizationProviderCfg> provider :
         DirectoryServer.getSynchronizationProviders())
    {
      provider.processSchemaChange(mods);
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/StressTest.java
@@ -231,14 +231,6 @@
    // Multimaster Synchro plugin
    synchroPluginStringDN = "cn=Multimaster Synchronization, "
        + synchroStringDN;
    String synchroPluginLdif = "dn: "
        + synchroPluginStringDN
        + "\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-provider\n"
        + "ds-cfg-synchronization-provider-enabled: true\n"
        + "ds-cfg-synchronization-provider-class: org.opends.server.synchronization.MultimasterSynchronization\n";
    synchroPluginEntry = TestCaseUtils.entryFromLdifString(synchroPluginLdif);
    // Change log
    changeLogStringDN = "cn=Changelog Server, " + synchroPluginStringDN;
@@ -250,7 +242,7 @@
    changeLogEntry = TestCaseUtils.entryFromLdifString(changeLogLdif);
    // suffix synchronized
    synchroServerStringDN = "cn=example, " + synchroPluginStringDN;
    synchroServerStringDN = "cn=example, cn=domains, " + synchroPluginStringDN;
    String synchroServerLdif = "dn: " + synchroServerStringDN + "\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-provider-config\n"
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationTestCase.java
@@ -28,7 +28,6 @@
import static org.opends.server.loggers.Error.logError;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.net.SocketException;
@@ -41,8 +40,6 @@
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.schema.IntegerSyntax;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
@@ -50,7 +47,6 @@
import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.synchronization.common.ServerState;
import org.opends.server.synchronization.plugin.ChangelogBroker;
import org.opends.server.synchronization.plugin.MultimasterSynchronization;
import org.opends.server.synchronization.plugin.PersistentServerState;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
@@ -84,8 +80,7 @@
   * Created entries that need to be deleted for cleanup
   */
  protected LinkedList<DN> entryList = new LinkedList<DN>();
  protected Entry synchroPluginEntry;
  protected LinkedList<DN> configEntryList = new LinkedList<DN>();
  protected Entry synchroServerEntry;
@@ -97,14 +92,10 @@
  protected boolean schemaCheck;
  /**
   *
   */
  MultimasterSynchronization mms = null;
  /**
   * The synchronization plugin entry
   */
  protected String synchroPluginStringDN;
  protected String synchroPluginStringDN =
    "cn=Multimaster Synchronization, cn=Synchronization Providers,cn=config";
  /**
   * Set up the environment for performing the tests in this suite.
@@ -228,20 +219,19 @@
        }
      }
      catch (Exception e)
      {
      }
      { }
    }
    return broker;
  }
  /**
   * suppress all the entries created by the tests in this class
   * suppress all the config entries created by the tests in this class
   */
  protected void cleanEntries()
  protected void cleanConfigEntries()
  {
    logError(ErrorLogCategory.SYNCHRONIZATION,
        ErrorLogSeverity.NOTICE,
        "SynchronizationTestCase/Cleaning entries" , 1);
        "SynchronizationTestCase/Cleaning config entries" , 1);
    DeleteOperation op;
    // Delete entries
@@ -249,6 +239,38 @@
    {
      while (true)
      {
        DN dn = configEntryList.removeLast();
             logError(ErrorLogCategory.SYNCHRONIZATION,
            ErrorLogSeverity.NOTICE,
            "cleaning config entry " + dn, 1);
        op = new DeleteOperation(connection, InternalClientConnection
            .nextOperationID(), InternalClientConnection.nextMessageID(), null,
            dn);
        op.run();
      }
    }
    catch (NoSuchElementException e) {
      // done
    }
  }
  /**
   * suppress all the real entries created by the tests in this class
   */
  protected void cleanRealEntries()
  {
    logError(ErrorLogCategory.SYNCHRONIZATION,
        ErrorLogSeverity.NOTICE,
        "SynchronizationTestCase/Cleaning entries" , 1);
    DeleteOperation op;
    // Delete entries
    try
    {
      while (true)
      {
        DN dn = entryList.removeLast();
        logError(ErrorLogCategory.SYNCHRONIZATION,
            ErrorLogSeverity.NOTICE,
@@ -277,20 +299,8 @@
  {
    DirectoryServer.setCheckSchema(schemaCheck);
    // WORKAROUND FOR BUG #639 - BEGIN -
    if (mms != null)
    {
      logError(ErrorLogCategory.SYNCHRONIZATION,
          ErrorLogSeverity.NOTICE,
          "SynchronizationTestCase/FinalizeSynchronization Provider" , 1);
      DirectoryServer.deregisterSynchronizationProvider(mms);
      mms.finalizeSynchronizationProvider();
      mms = null;
    }
    // WORKAROUND FOR BUG #639 - END -
    cleanEntries();
    cleanConfigEntries();
    cleanRealEntries();
  }
  /**
@@ -298,48 +308,45 @@
   */
  protected void configureSynchronization() throws Exception
  {
    //
    // Add the Multimaster synchronization plugin
    String synchroPluginLdif = "dn: " + synchroPluginStringDN + "\n"
         + "objectClass: top\n"
         + "objectClass: ds-cfg-synchronization-provider\n"
         + "objectClass: ds-cfg-multimaster-synchronization-provider\n"
         + "ds-cfg-synchronization-provider-enabled: true\n"
         + "ds-cfg-synchronization-provider-class: " +
         "org.opends.server.synchronization.plugin.MultimasterSynchronization\n";
    Entry synchroPluginEntry = TestCaseUtils.entryFromLdifString(synchroPluginLdif);
    DirectoryServer.getConfigHandler().addEntry(synchroPluginEntry, null);
    entryList.add(synchroPluginEntry.getDN());
    configEntryList.add(synchroPluginEntry.getDN());
    assertNotNull(DirectoryServer.getConfigEntry(DN
        .decode(synchroPluginStringDN)),
        "Unable to add the Multimaster synchronization plugin");
    // WORKAROUND FOR BUG #639 - BEGIN -
    DN dn = DN.decode(synchroPluginStringDN);
    ConfigEntry mmsConfigEntry = DirectoryServer.getConfigEntry(dn);
    mms = new MultimasterSynchronization();
    try
    {
      mms.initializeSynchronizationProvider(mmsConfigEntry);
    }
    catch (ConfigException e)
    {
      assertTrue(false,
          "Unable to initialize the Multimaster synchronization plugin");
    }
    DirectoryServer.registerSynchronizationProvider(mms);
    // WORKAROUND FOR BUG #639 - END -
    //
    // Add the changelog server
    if (changeLogEntry!=null)
    {
      DirectoryServer.getConfigHandler().addEntry(changeLogEntry, null);
      assertNotNull(DirectoryServer.getConfigEntry(changeLogEntry.getDN()),
        "Unable to add the changeLog server");
      entryList.add(changeLogEntry.getDN());
    }
    
    if (synchroServerEntry!=null)
    {
      // We also have a replicated suffix (synchronization domain)
      DirectoryServer.getConfigHandler().addEntry(synchroServerEntry, null);
      assertNotNull(DirectoryServer.getConfigEntry(synchroServerEntry.getDN()),
      "Unable to add the synchronized suffix");
      entryList.add(synchroServerEntry.getDN());
    }
    // domains container entry.
    String domainsLdif = "dn: "
      + "cn=domains," + synchroPluginStringDN + "\n"
      + "objectClass: top\n"
      + "objectClass: ds-cfg-branch\n";
    Entry domainsEntry = TestCaseUtils.entryFromLdifString(domainsLdif);
    DirectoryServer.getConfigHandler().addEntry(domainsEntry, null);
    configEntryList.add(domainsEntry.getDN());
    assertNotNull(DirectoryServer.getConfigEntry(
      DN.decode(synchroPluginStringDN)),
      "Unable to add the Multimaster synchronization plugin");
    // Add the changelog server
    DirectoryServer.getConfigHandler().addEntry(changeLogEntry, null);
    assertNotNull(DirectoryServer.getConfigEntry(changeLogEntry.getDN()),
       "Unable to add the changeLog server");
    configEntryList.add(changeLogEntry.getDN());
    // We also have a replicated suffix (synchronization domain)
    DirectoryServer.getConfigHandler().addEntry(synchroServerEntry, null);
    assertNotNull(DirectoryServer.getConfigEntry(synchroServerEntry.getDN()),
        "Unable to add the synchronized server");
    configEntryList.add(synchroServerEntry.getDN());
  }
  /**
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java
@@ -133,14 +133,6 @@
    // Multimaster Synchro plugin
    synchroPluginStringDN = "cn=Multimaster Synchronization, "
        + synchroStringDN;
    String synchroPluginLdif = "dn: "
        + synchroPluginStringDN
        + "\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-provider\n"
        + "ds-cfg-synchronization-provider-enabled: true\n"
        + "ds-cfg-synchronization-provider-class: org.opends.server.synchronization.MultimasterSynchronization\n";
    synchroPluginEntry = TestCaseUtils.entryFromLdifString(synchroPluginLdif);
    // Change log
    String changeLogStringDN = "cn=Changelog Server, " + synchroPluginStringDN;
@@ -152,7 +144,8 @@
    changeLogEntry = TestCaseUtils.entryFromLdifString(changeLogLdif);
    // suffix synchronized
    String synchroServerStringDN = "cn=example, " + synchroPluginStringDN;
    String synchroServerStringDN =
      "cn=example, cn=domains, " + synchroPluginStringDN;
    String synchroServerLdif = "dn: " + synchroServerStringDN + "\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-provider-config\n"
@@ -318,7 +311,7 @@
        ErrorLogSeverity.NOTICE,
        "Starting synchronization test : lostHeartbeatFailover" , 1);
    cleanEntries();
    cleanRealEntries();
    final DN baseDn = DN.decode("ou=People,dc=example,dc=com");
@@ -831,7 +824,7 @@
    final DN baseDn = DN.decode("ou=People,dc=example,dc=com");
    cleanEntries();
    cleanRealEntries();
    ChangelogBroker broker =
      openChangelogSession(baseDn, (short) 27, 100, 8989, 1000, true);
@@ -886,7 +879,7 @@
      "The received synchronization message is not a MODIFY msg");
      ModifyMsg modMsg = (ModifyMsg) msg;
      Operation receivedOp = modMsg.createOperation(connection);
      modMsg.createOperation(connection);
      assertTrue(DN.decode(modMsg.getDn()).compareTo(personEntry.getDN()) == 0,
      "The received MODIFY synchronization message is not for the excepted DN");
@@ -908,7 +901,7 @@
      assertTrue(msg instanceof ModifyDNMsg,
      "The received synchronization message is not a MODIFY DN msg");
      ModifyDNMsg moddnMsg = (ModifyDNMsg) msg;
      receivedOp = moddnMsg.createOperation(connection);
      moddnMsg.createOperation(connection);
      assertTrue(DN.decode(moddnMsg.getDn()).compareTo(personEntry.getDN()) == 0,
      "The received MODIFY_DN message is not for the excepted DN");
@@ -927,7 +920,7 @@
      assertTrue(msg instanceof DeleteMsg,
      "The received synchronization message is not a MODIFY DN msg");
      DeleteMsg delMsg = (DeleteMsg) msg;
      receivedOp = delMsg.createOperation(connection);
      delMsg.createOperation(connection);
      assertTrue(DN.decode(delMsg.getDn()).compareTo(DN
          .decode("uid= new person,ou=People,dc=example,dc=com")) == 0,
      "The received DELETE message is not for the excepted DN");
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/changelog/ChangelogFakeConfiguration.java
New file
@@ -0,0 +1,190 @@
/*
 * 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.synchronization.changelog;
import java.util.SortedSet;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.client.ChangelogServerCfgClient;
import org.opends.server.admin.std.server.ChangelogServerCfg;
import org.opends.server.types.DN;
/**
 * This Class implements an object that can be used to instantiate
 * The Changelog class for tests purpose.
 */
public class ChangelogFakeConfiguration implements ChangelogServerCfg
{
  int port;
  String dirName;
  int purgeDelay;
  int serverId;
  int queueSize;
  int windowSize;
  private SortedSet<String> servers;
  public ChangelogFakeConfiguration(
      int port, String dirName, int purgeDelay, int serverId,
      int queueSize, int windowSize, SortedSet<String> servers)
  {
    this.port    = port;
    this.dirName = dirName;
    if (purgeDelay == 0)
    {
      this.purgeDelay = 24*60*60;
    }
    else
    {
      this.purgeDelay = purgeDelay;
    }
    this.serverId = serverId;
    if (queueSize == 0)
    {
      this.queueSize = 10000;
    }
    else
    {
      this.queueSize = queueSize;
    }
    if (windowSize == 0)
    {
      this.windowSize = 100;
    }
    else
    {
      this.windowSize = windowSize;
    }
    this.servers = servers;
  }
  /**
   * {@inheritDoc}
   */
  public void addChangeListener(
      ConfigurationChangeListener<ChangelogServerCfg> listener)
  {
  }
  /**
   * {@inheritDoc}
   */
  public ManagedObjectDefinition<? extends ChangelogServerCfgClient,
                                 ? extends ChangelogServerCfg> definition()
  {
    return null;
  }
  /**
   * {@inheritDoc}
   */
  public String getChangelogDbDirectory()
  {
    return dirName;
  }
  /**
   * {@inheritDoc}
   */
  public int getChangelogPort()
  {
    return port;
  }
  /**
   * {@inheritDoc}
   */
  public long getChangelogPurgeDelay()
  {
    return purgeDelay;
  }
  /**
   * {@inheritDoc}
   */
  public SortedSet<String> getChangelogServer()
  {
     return servers;
  }
  /**
   * {@inheritDoc}
   */
  public int getChangelogServerId()
  {
    return serverId;
  }
  /**
   * {@inheritDoc}
   */
  public int getQueueSize()
  {
    return queueSize;
  }
  /**
   * {@inheritDoc}
   */
  public int getWindowSize()
  {
    return windowSize;
  }
  /**
   * {@inheritDoc}
   */
  public void removeChangeListener(
      ConfigurationChangeListener<ChangelogServerCfg> listener)
  {
  }
  /**
   * {@inheritDoc}
   */
  public DN dn()
  {
    return null;
  }
  /**
   * {@inheritDoc}
   */
  public PropertyProvider properties()
  {
    return null;
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/changelog/ChangelogTest.java
@@ -34,9 +34,10 @@
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.server.TestCaseUtils;
import org.opends.server.config.ConfigEntry;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.synchronization.SynchronizationTestCase;
import org.opends.server.synchronization.common.ChangeNumber;
@@ -98,17 +99,9 @@
    changelogPort = socket.getLocalPort();
    socket.close();
    String changelogLdif =
      "dn: cn=Changelog Server\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-changelog-server-config\n"
        + "cn: Changelog Server\n"
        + "ds-cfg-changelog-port: "+ changelogPort + "\n"
        + "ds-cfg-changelog-server-id: 1\n"
        + "ds-cfg-window-size: 100";
    Entry tmp = TestCaseUtils.entryFromLdifString(changelogLdif);
    ConfigEntry changelogConfig = new ConfigEntry(tmp, null);
    changelog = new Changelog(changelogConfig);
    ChangelogFakeConfiguration conf =
      new ChangelogFakeConfiguration(changelogPort, null, 0, 1, 0, 0, null);
    changelog = new Changelog(conf);
  }
  /**
@@ -602,19 +595,13 @@
        // for itest=0, create the 2 connected changelog servers
        // for itest=1, create the 1rst changelog server, the second
        // one will be created later
        String changelogLdif = "dn: cn=Changelog Server\n"
          + "objectClass: top\n"
          + "objectClass: ds-cfg-synchronization-changelog-server-config\n"
          + "cn: Changelog Server\n"
          + "ds-cfg-changelog-port: " + changelogPorts[i] + "\n"
          + "ds-cfg-changelog-server: localhost:" + ((i == 0) ? changelogPorts[1] : changelogPorts[0]) + "\n"
          + "ds-cfg-changelog-server-id: " + changelogIds[0] + "\n"
          + "ds-cfg-window-size: 100" + "\n"
          + "ds-cfg-changelog-db-directory: changelogDb"+i;
        Entry tmp = TestCaseUtils.entryFromLdifString(changelogLdif);
        ConfigEntry changelogConfig = new ConfigEntry(tmp, null);
        changelogs[i] = new Changelog(changelogConfig);
        SortedSet<String> servers = new TreeSet<String>();
        servers.add(
          "localhost:" + ((i == 0) ? changelogPorts[1] : changelogPorts[0]));
        ChangelogFakeConfiguration conf =
          new ChangelogFakeConfiguration(changelogPorts[i], "changelogDb"+i, 0,
                                         changelogIds[i], 0, 100, servers);
        changelog = new Changelog(conf);
      }
      ChangelogBroker broker1 = null;
@@ -681,16 +668,13 @@
        if (itest > 0)
        {
          socket.close();
          String changelogLdif = "dn: cn=Changelog Server\n"
            + "objectClass: top\n"
            + "objectClass: ds-cfg-synchronization-changelog-server-config\n"
            + "cn: Changelog Server\n"
            + "ds-cfg-changelog-port: " + changelogPorts[1] + "\n"
            + "ds-cfg-changelog-server: localhost:" + changelogPorts[0] + "\n"
            + "ds-cfg-changelog-server-id: " + changelogIds[1] + "\n";
          Entry tmp = TestCaseUtils.entryFromLdifString(changelogLdif);
          ConfigEntry changelogConfig = new ConfigEntry(tmp, null);
          changelogs[1] = new Changelog(changelogConfig);
          SortedSet<String> servers = new TreeSet<String>();
          servers.add("localhost:"+changelogPorts[0]);
          ChangelogFakeConfiguration conf =
            new ChangelogFakeConfiguration(changelogPorts[1], null, 0,
                                           changelogIds[1], 0, 0, null);
          changelogs[1] = new Changelog(conf);
          // Connect broker 2 to changelog2
          broker2 = openChangelogSession(DN.decode("dc=example,dc=com"),
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/changelog/dbHandlerTest.java
@@ -30,13 +30,11 @@
import java.net.ServerSocket;
import org.opends.server.TestCaseUtils;
import org.opends.server.config.ConfigEntry;
import org.opends.server.synchronization.SynchronizationTestCase;
import org.opends.server.synchronization.common.ChangeNumber;
import org.opends.server.synchronization.common.ChangeNumberGenerator;
import org.opends.server.synchronization.protocol.DeleteMsg;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
@@ -56,17 +54,10 @@
    socket.close();
    // configure a Changelog server.
    String changelogLdif =
      "dn: cn=Changelog Server\n"
        + "objectClass: top\n"
        + "objectClass: ds-cfg-synchronization-changelog-server-config\n"
        + "cn: Changelog Server\n"
        + "ds-cfg-changelog-port: "+ changelogPort + "\n"
        + "ds-cfg-changelog-server-id: 2\n"
        + "ds-cfg-changelog-purge-delay: 0 d";
    Entry tmp = TestCaseUtils.entryFromLdifString(changelogLdif);
    ConfigEntry changelogConfig = new ConfigEntry(tmp, null);
    Changelog changelog = new Changelog(changelogConfig);
    ChangelogFakeConfiguration conf =
      new ChangelogFakeConfiguration(changelogPort, null, 0,
                                     2, 0, 100, null);
    Changelog changelog = new Changelog(conf);
    // create or clean a directory for the dbHandler
    String buildRoot = System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT);
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/plugin/HistoricalTest.java
@@ -67,22 +67,6 @@
    // Create an internal connection.
    connection = InternalClientConnection.getRootConnection();
    // Top level synchronization provider.
    String synchroStringDN = "cn=Synchronization Providers,cn=config";
    // Multimaster synchronization plugin.
    synchroPluginStringDN = "cn=Multimaster Synchronization, "
         + synchroStringDN;
    String synchroPluginLdif = "dn: "
         + synchroPluginStringDN
         + "\n"
         + "objectClass: top\n"
         + "objectClass: ds-cfg-synchronization-provider\n"
         + "ds-cfg-synchronization-provider-enabled: true\n"
         + "ds-cfg-synchronization-provider-class: " +
         "org.opends.server.synchronization.MultimasterSynchronization\n";
    synchroPluginEntry = TestCaseUtils.entryFromLdifString(synchroPluginLdif);
    // The synchronization server.
    String changeLogStringDN = "cn=Changelog Server, " + synchroPluginStringDN;
    String changeLogLdif = "dn: " + changeLogStringDN + "\n"
@@ -93,7 +77,7 @@
    changeLogEntry = TestCaseUtils.entryFromLdifString(changeLogLdif);
    // The suffix to be synchronized.
    String synchroServerStringDN = "o=test, " + synchroPluginStringDN;
    String synchroServerStringDN = "o=test, cn=domains, " + synchroPluginStringDN;
    String synchroServerLdif = "dn: " + synchroServerStringDN + "\n"
         + "objectClass: top\n"
         + "objectClass: ds-cfg-synchronization-provider-config\n"
opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/plugin/ModifyConflictTest.java
@@ -38,7 +38,6 @@
import static org.opends.server.synchronization.protocol.OperationContext.*;
import org.opends.server.TestAccessLogger;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;