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

mrossign
16.34.2009 55a07ce2479e1b4c74dec15ce4e78e3fdf50a27c
opends/resource/config/config.ldif
@@ -1841,6 +1841,7 @@
ds-cfg-java-class: org.opends.server.replication.plugin.FractionalLDIFImportPlugin
ds-cfg-enabled: true
ds-cfg-plugin-type: ldifImport
ds-cfg-plugin-type: ldifImportEnd
ds-cfg-invoke-for-internal-operations: true
dn: cn=Root DNs,cn=config
opends/resource/schema/02-config.ldif
@@ -2439,6 +2439,11 @@
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  USAGE directoryOperation
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.599
  NAME 'ds-cfg-plugin-order-ldif-import-end'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler'
  SUP top
@@ -3496,6 +3501,7 @@
        ds-cfg-plugin-order-post-connect $
        ds-cfg-plugin-order-post-disconnect $
        ds-cfg-plugin-order-ldif-import $
        ds-cfg-plugin-order-ldif-import-end $
        ds-cfg-plugin-order-ldif-export $
        ds-cfg-plugin-order-pre-parse-abandon $
        ds-cfg-plugin-order-pre-parse-add $
opends/src/admin/defn/org/opends/server/admin/std/PluginConfiguration.xml
@@ -23,7 +23,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Copyright 2007-2009 Sun Microsystems, Inc.
  ! -->
<adm:managed-object name="plugin" plural-name="plugins"
  package="org.opends.server.admin.std"
@@ -116,6 +116,11 @@
            Invoked for each entry read during an LDIF import.
          </adm:synopsis>
        </adm:value>
        <adm:value name="ldifimportend">
          <adm:synopsis>
            Invoked at the end of an LDIF import session.
          </adm:synopsis>
        </adm:value>
        <adm:value name="ldifexport">
          <adm:synopsis>
            Invoked for each operation to be written during an LDIF
opends/src/admin/defn/org/opends/server/admin/std/PluginRootConfiguration.xml
@@ -23,7 +23,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Copyright 2007-2009 Sun Microsystems, Inc.
  ! -->
<adm:managed-object name="plugin-root" plural-name="plugin-roots"
  package="org.opends.server.admin.std"
@@ -204,6 +204,35 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="plugin-order-ldif-import-end">
    <adm:synopsis>
      Specifies the order in which LDIF import end plug-ins are to be loaded
      and invoked.
    </adm:synopsis>
    <adm:description>
      The value is a comma-delimited list of plug-in
      names (where the plug-in name is the RDN value from the plug-in
      configuration entry DN). The list can include at most one asterisk
      to indicate the position of any unspecified plug-in (and the
      relative order of those unspecified plug-ins is undefined).
    </adm:description>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          The order in which LDIF import end plug-ins are loaded and invoked
          is undefined.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-plugin-order-ldif-import-end</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="plugin-order-ldif-export">
    <adm:synopsis>
      Specifies the order in which LDIF export plug-ins are to be loaded
opends/src/messages/messages/jeb.properties
@@ -343,7 +343,7 @@
NOTICE_JEB_IMPORT_LDIF_LOG_BYTES_184=Setting DB log byte size to %d bytes
NOTICE_JEB_IMPORT_LDIF_DB_MEM_BUF_INFO_185=Setting DB cache size to %d bytes \
 and phase one buffer size to to %d bytes
NOTICE_JEB_IMPORT_LDIF_TOT_MEM_BUF_186=The amount of freeemory available to \
NOTICE_JEB_IMPORT_LDIF_TOT_MEM_BUF_186=The amount of free memory available to \
the import task is %d bytes. The number of phase one buffers required is \
%d buffers
NOTICE_JEB_IMPORT_LDIF_BUFFER_CHECKPOINTS_187=Checkpoints performed: %d
opends/src/messages/messages/replication.properties
@@ -414,5 +414,13 @@
 database of the draft change number : %s
SEVERE_ERR_INITIALIZATION_FAILED_NOCONN_174=The initialization failed because \
 the domain %s is not connected to a replication server
SEVERE_ERR_FRACTIONAL_COULD_NOT_RETRIEVE_CONFIG_175=Could not retrieve the \
 configuration for a replication domain matching the entry %s
NOTICE_ERR_LDIF_IMPORT_FRACTIONAL_BAD_DATA_SET_176=The LDIF import for \
 importing suffix %s data has been stopped due to fractional configuration \
 inconsistency : imported data set has not the same fractional configuration \
 as local server
NOTICE_ERR_LDIF_IMPORT_FRACTIONAL_DATA_SET_IS_FRACTIONAL_177=The LDIF import \
 for importing suffix %s data has been stopped due to fractional configuration \
 inconsistency : imported data set has some fractional configuration but not \
 local server
opends/src/server/org/opends/server/api/plugin/DirectoryServerPlugin.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.api.plugin;
import org.opends.messages.Message;
@@ -312,7 +312,6 @@
  }
  /**
   * Performs any necessary processing that should be done during an
   * LDIF import operation immediately after reading an entry and
@@ -333,7 +332,20 @@
    throw new UnsupportedOperationException(message.toString());
  }
  /**
   * Terminates an import session.
   * Performs any necessary processing that should be done at the end
   * of an LDIF import session based on the provided configuration.
   *
   * @param  importConfig  The configuration used for the LDIF import.
   */
  public void doLDIFImportEnd(LDIFImportConfig importConfig)
  {
    Message message = ERR_PLUGIN_TYPE_NOT_SUPPORTED.get(
        String.valueOf(pluginDN),
        PluginType.LDIF_IMPORT_END.getName());
    throw new UnsupportedOperationException(message.toString());
  }
  /**
   * Performs any necessary processing that should be done during an
opends/src/server/org/opends/server/api/plugin/PluginType.java
@@ -86,6 +86,14 @@
  /**
   * The plugin type for plugins that are to be invoked for each
   * import session end.
   */
  LDIF_IMPORT_END("ldifimportend"),
  /**
   * The plugin type for plugins that are to be invoked for each entry
   * written during an LDIF export.
   */
opends/src/server/org/opends/server/core/PluginConfigManager.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.core;
@@ -90,6 +90,7 @@
  private DirectoryServerPlugin[] postConnectPlugins;
  private DirectoryServerPlugin[] postDisconnectPlugins;
  private DirectoryServerPlugin[] ldifImportPlugins;
  private DirectoryServerPlugin[] ldifImportEndPlugins;
  private DirectoryServerPlugin[] ldifExportPlugins;
  private DirectoryServerPlugin[] preParseAbandonPlugins;
  private DirectoryServerPlugin[] preParseAddPlugins;
@@ -174,6 +175,7 @@
    postConnectPlugins                 = new DirectoryServerPlugin[0];
    postDisconnectPlugins              = new DirectoryServerPlugin[0];
    ldifImportPlugins                  = new DirectoryServerPlugin[0];
    ldifImportEndPlugins               = new DirectoryServerPlugin[0];
    ldifExportPlugins                  = new DirectoryServerPlugin[0];
    preParseAbandonPlugins             = new DirectoryServerPlugin[0];
    preParseAddPlugins                 = new DirectoryServerPlugin[0];
@@ -423,6 +425,7 @@
      case POSTCONNECT:            return PluginType.POST_CONNECT;
      case POSTDISCONNECT:         return PluginType.POST_DISCONNECT;
      case LDIFIMPORT:             return PluginType.LDIF_IMPORT;
      case LDIFIMPORTEND:          return PluginType.LDIF_IMPORT_END;
      case LDIFEXPORT:             return PluginType.LDIF_EXPORT;
      case PREPARSEABANDON:        return PluginType.PRE_PARSE_ABANDON;
      case PREPARSEADD:            return PluginType.PRE_PARSE_ADD;
@@ -595,6 +598,11 @@
                 addPlugin(ldifImportPlugins, plugin, t,
                           pluginRootConfig.getPluginOrderLDIFImport());
            break;
          case LDIF_IMPORT_END:
            ldifImportEndPlugins =
                 addPlugin(ldifImportEndPlugins, plugin, t,
                           pluginRootConfig.getPluginOrderLDIFImportEnd());
            break;
          case LDIF_EXPORT:
            ldifExportPlugins =
                 addPlugin(ldifExportPlugins, plugin, t,
@@ -1088,6 +1096,9 @@
          case LDIF_IMPORT:
            ldifImportPlugins = removePlugin(ldifImportPlugins, plugin);
            break;
          case LDIF_IMPORT_END:
            ldifImportEndPlugins = removePlugin(ldifImportEndPlugins, plugin);
            break;
          case LDIF_EXPORT:
            ldifExportPlugins = removePlugin(ldifExportPlugins, plugin);
            break;
@@ -1558,7 +1569,7 @@
  /**
/**
   * Invokes the set of LDIF import plugins that have been configured in the
   * Directory Server.
   *
@@ -1622,6 +1633,24 @@
  /**
   * Invokes the LDIF import session finalization of LDIF import plugins that
   * have been configured in the Directory Server.
   *
   * @param  importConfig  The LDIF import configuration used for the LDIF
   *                       import session.
   */
  public void invokeLDIFImportEndPlugins(
      LDIFImportConfig importConfig)
  {
    for (DirectoryServerPlugin p : ldifImportEndPlugins)
    {
      p.doLDIFImportEnd(importConfig);
    }
  }
  /**
   * Invokes the set of LDIF export plugins that have been configured in the
   * Directory Server.
   *
opends/src/server/org/opends/server/replication/plugin/FractionalLDIFImportPlugin.java
@@ -27,6 +27,7 @@
package org.opends.server.replication.plugin;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -34,24 +35,31 @@
import org.opends.messages.Message;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ServerManagementContext;
import org.opends.server.admin.std.server.FractionalLDIFImportPluginCfg;
import org.opends.server.admin.std.server.PluginCfg;
import org.opends.server.admin.std.server.ReplicationDomainCfg;
import org.opends.server.admin.std.server.ReplicationSynchronizationProviderCfg;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.api.plugin.*;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.replication.plugin.LDAPReplicationDomain.
  AttributeValueStringIterator;
import org.opends.server.replication.plugin.LDAPReplicationDomain.
  FractionalConfig;
import org.opends.server.types.*;
import static org.opends.messages.ReplicationMessages.*;
/**
 * This class implements a Directory Server plugin that is used in fractional
 * replication when an online full update occurs.
 * replication to initialize a just configured fractional domain (when an online
 * full update occurs or offline/online ldif import).
 * The following tasks are done:
 * - check that the fractional configuration (if any) stored in the root entry
 * of the domain is compliant with the fractional configuration of the domain
 *  (if not make online update stop)
 * - check that the fractional configuration (if any) stored in the (incoming)
 * root entry of the domain is compliant with the fractional configuration of
 * the domain (if not make online update stop)
 * - perform filtering according to fractional configuration of the domain
 * - flush the fractional configuration of the domain in the root entry
 *  (if no one already present)
@@ -60,6 +68,57 @@
  extends DirectoryServerPlugin<FractionalLDIFImportPluginCfg>
  implements ConfigurationChangeListener<FractionalLDIFImportPluginCfg>
{
  // Holds the fractional configuration and if available the replication domain
  // matching this import session (they form the importfractional context).
  // Domain is available if the server is online (import-ldif, online full
  // update..) otherwise, this is an import-ldif with server off. The key is the
  // ImportConfig object of the session which acts as a cookie for the whole
  // session. This allows to potentially run man imports at the same time.
  private final Hashtable<LDIFImportConfig, ImportFractionalContext>
    importSessionContexts = new Hashtable<LDIFImportConfig,
    ImportFractionalContext>();
  /**
   * Holds an import session fractional context.
   */
  private static class ImportFractionalContext
  {
    // Fractional configuration of the local domain (may be null if import on a
    // not replicated domain)
    private FractionalConfig fractionalConfig = null;
    // The local domain object (may stay null if server is offline)
    private LDAPReplicationDomain domain = null;
    /**
     * Constructor.
     * @param fractionalConfig
     * @param domain
     */
    public ImportFractionalContext(FractionalConfig fractionalConfig,
      LDAPReplicationDomain domain)
    {
      this.fractionalConfig = fractionalConfig;
      this.domain = domain;
    }
    /**
     * Getter for the fractional configuration.
     * @return the fractionalConfig
     */
    public FractionalConfig getFractionalConfig()
    {
      return fractionalConfig;
    }
    /**
     * Getter for the domain..
     * @return the domain
     */
    public LDAPReplicationDomain getDomain()
    {
      return domain;
    }
  }
  /**
   * Creates a new instance of this Directory Server plugin.  Every plugin must
@@ -86,6 +145,7 @@
      switch (t)
      {
        case LDIF_IMPORT:
        case LDIF_IMPORT_END:
          // This is acceptable.
          break;
@@ -104,9 +164,81 @@
  @Override()
  public final void finalizePlugin()
  {
    // Nothing to do
  }
  /**
   * Attempts to retrieve the fractional configuration of the domain being
   * imported.
   * @param entry An imported entry of the imported domain
   * @return The parsed fractional configuration for the domain matching the
   * passed entry. Null if no configuration is found for the domain
   * (not a replicated domain).
   */
  private static FractionalConfig getStaticReplicationDomainFractionalConfig(
    Entry entry) throws Exception {
    // Retrieve the configuration
    ServerManagementContext context = ServerManagementContext.getInstance();
    RootCfg root = context.getRootConfiguration();
    ReplicationSynchronizationProviderCfg sync =
      (ReplicationSynchronizationProviderCfg)
      root.getSynchronizationProvider("Multimaster Synchronization");
    String[] domainNames = sync.listReplicationDomains();
    if (domainNames == null)
    {
      // No domain in replication
      return null;
    }
    // Find the configuration for domain the entry is part of
    ReplicationDomainCfg matchingReplicatedDomainCfg = null;
    for (String domainName : domainNames)
    {
      // Get the domain configuration object
      ReplicationDomainCfg replicationDomainCfg =
      sync.getReplicationDomain(domainName);
      // Is the entry a sub entry of the replicated domain main entry ?
      DN replicatedDn = replicationDomainCfg.getBaseDN();
      DN entryDn = entry.getDN();
      if (entryDn.isDescendantOf(replicatedDn))
      {
        // Found the matching replicated domain configuration object
        matchingReplicatedDomainCfg = replicationDomainCfg;
        break;
      }
    }
    if (matchingReplicatedDomainCfg == null)
    {
      // No matching replicated domain found
      return null;
    }
    // Extract the fractional configuration from the domain configuration object
    // and return it.
    return FractionalConfig.toFractionalConfig(matchingReplicatedDomainCfg);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void doLDIFImportEnd(
    LDIFImportConfig importConfig)
  {
    // Remove the cookie of this import session
    synchronized(importSessionContexts)
    {
      importSessionContexts.remove(importConfig);
    }
  }
  /**
   * See class comment for what we achieve here...
   * {@inheritDoc}
   */
  @Override()
@@ -114,24 +246,93 @@
    LDIFImportConfig importConfig, Entry entry)
  {
    /**
     * See class comment for what we achieve here...
     * try to get the import fractional context for this entry. If not found,
     * create and initialize it. The mechanism here is done to take a lock only
     * once for the whole import session (except the necessary lock of the
     * doLDIFImportEnd method)
     */
    ImportFractionalContext importFractionalContext =
      importSessionContexts.get(importConfig);
    // Retrieve the replicated domain this entry belongs to
    DN entryDn = entry.getDN();
    LDAPReplicationDomain domain = MultimasterReplication.findDomain(entryDn,
      null);
    if (domain == null)
    FractionalConfig localFractionalConfig = null;
    // If no context, create it
    if (importFractionalContext == null)
    {
      synchronized(importSessionContexts)
      {
        // Insure antoher thread was not creating the context at the same time
        // (we would create it for the second time which is useless)
        importFractionalContext = importSessionContexts.get(importConfig);
        if (importFractionalContext == null)
        {
          /*
           * Create context
           */
          /**
           * Retrieve the replicated domain this entry belongs to. Try to
           * retrieve replication domain instance first. If we are in an online
           * server, we should get it (if we are treating an entry that belongs
           * to a replicated domain), otherwise the domain is not replicated or
           * we are in an offline server context (import-ldif command run with
           * offline server) and we must retrieve the fractional configuration
           * directly from the configuration management system.
           */
          LDAPReplicationDomain domain =
            MultimasterReplication.findDomain(entryDn, null);
          // Get the fractional configuration extracted from the local server
          // configuration for the currently imported domain
          if (domain == null)
          {
            // Server may be offline, attempt to find fractional configuration
            // from config sub-system
            try
            {
              localFractionalConfig =
                getStaticReplicationDomainFractionalConfig(entry);
            } catch (Exception ex)
            {
              Message message = ERR_FRACTIONAL_COULD_NOT_RETRIEVE_CONFIG.get(
                entry.toString());
              return PluginResult.ImportLDIF.stopEntryProcessing(message);
            }
          } else
          {
            // Found a live domain, retrieve the fractional configuration from
            // it.
            localFractionalConfig = domain.getFractionalConfig();
          }
          // Create context and store it
          importFractionalContext =
            new ImportFractionalContext(localFractionalConfig, domain);
          importSessionContexts.put(importConfig, importFractionalContext);
        }
      }
    }
    // Extract the fractional configuration from the context
    localFractionalConfig = importFractionalContext.getFractionalConfig();
    if (localFractionalConfig == null)
    {
      // Not part of a replicated domain : nothing to do
      return PluginResult.ImportLDIF.continueEntryProcessing();
    }
    // Is the entry to treat the root entry of the domain ? If yes, analyze the
    // fractional configuration in it and compare with local domain fractional
    // configuration. Stop the import if some inconsistency is detcted
    DN domainBaseDn = domain.getBaseDN();
    if (domainBaseDn.equals(entryDn))
    /**
     * At this point, either the domain instance has been found and we  use its
     * fractional configuration, or the server is offline and we use the parsed
     * fractional configuration. We differentiate both cases testing if domain
     * is null. We are also for sure handling an entry of a replicated suffix.
     */
    // Is the entry to handle the root entry of the domain ? If yes, analyze the
    // fractional configuration in it and compare with local fractional
    // configuration. Stop the import if some inconsistency is detected
    DN replicatedDomainBaseDn = localFractionalConfig.getBaseDn();
    if (replicatedDomainBaseDn.equals(entryDn))
    {
      /*
       * This is the root entry, try to read a fractional configuration from it
@@ -169,8 +370,9 @@
      }
      // Compare backend and local fractional configuration
      boolean sameConfig = domain.isFractionalConfigConsistent(exclIt, inclIt);
      if (domain.isFractional())
      boolean sameConfig = LDAPReplicationDomain.
        isFractionalConfigConsistent(localFractionalConfig, exclIt, inclIt);
      if (localFractionalConfig.isFractional())
      {
        // Local domain is fractional
        if (sameConfig)
@@ -190,19 +392,28 @@
          }
          if (remoteDomainHasSomeConfig)
          {
            // Local domain is fractional, remote domain has some config which
            // is different : stop import (error will be logged when import is
            // stopped)
            domain.setImportErrorMessageId(
              LDAPReplicationDomain.IMPORT_ERROR_MESSAGE_BAD_REMOTE);
            domain.setFollowImport(false);
            return PluginResult.ImportLDIF.continueEntryProcessing();
            LDAPReplicationDomain domain = importFractionalContext.getDomain();
            if (domain != null)
            {
              // Local domain is fractional, remote domain has some config which
              // is different : stop import (error will be logged when import is
              // stopped)
              domain.setImportErrorMessageId(
                LDAPReplicationDomain.IMPORT_ERROR_MESSAGE_BAD_REMOTE);
              domain.setFollowImport(false);
              return PluginResult.ImportLDIF.stopEntryProcessing(null);
            } else
            {
              Message message = NOTE_ERR_LDIF_IMPORT_FRACTIONAL_BAD_DATA_SET.
                get(replicatedDomainBaseDn.toString());
              return PluginResult.ImportLDIF.stopEntryProcessing(message);
            }
          } else
          {
            // Local domain is fractional but remote domain has no config :
            // flush local config into root entry and follow import with
            // filtering
            flushFractionalConfigIntoEntry(domain, entry);
            flushFractionalConfigIntoEntry(localFractionalConfig, entry);
          }
        }
      } else
@@ -215,20 +426,32 @@
          return PluginResult.ImportLDIF.continueEntryProcessing();
        } else
        {
          // Local domain is not fractional but remote one is : stop import :
          // local domain should be configured with the same config as remote
          // one
          domain.setImportErrorMessageId(
              LDAPReplicationDomain.IMPORT_ERROR_MESSAGE_REMOTE_IS_FRACTIONAL);
          domain.setFollowImport(false);
          return PluginResult.ImportLDIF.continueEntryProcessing();
          LDAPReplicationDomain domain = importFractionalContext.getDomain();
          if (domain != null)
          {
            // Local domain is not fractional but remote one is : stop import :
            // local domain should be configured with the same config as remote
            // one
            domain.setImportErrorMessageId(
                LDAPReplicationDomain.
                IMPORT_ERROR_MESSAGE_REMOTE_IS_FRACTIONAL);
            domain.setFollowImport(false);
            return PluginResult.ImportLDIF.stopEntryProcessing(null);
          } else
          {
            Message message =
              NOTE_ERR_LDIF_IMPORT_FRACTIONAL_DATA_SET_IS_FRACTIONAL.get(
                replicatedDomainBaseDn.toString());
            return PluginResult.ImportLDIF.stopEntryProcessing(message);
          }
        }
      }
    }
    // If we get here, local domain fractional configuration is enabled.
    // Now filter for potential attributes to be removed.
    domain.fractionalRemoveAttributesFromEntry(entry.getDN().getRDN(),
    LDAPReplicationDomain.fractionalRemoveAttributesFromEntry(
      localFractionalConfig, entry.getDN().getRDN(),
      entry.getObjectClasses(), entry.getUserAttributes(), true);
    return PluginResult.ImportLDIF.continueEntryProcessing();
@@ -240,20 +463,21 @@
   * WARNING: assumption is that no fractional attributes at all is already
   * present in the passed entry. Also assumption is that domain fractional
   * configuration is on.
   * @param domain Domain containing the fractional configuration to use
   * @param localFractionalConfig The local domain fractional configuration
   * @param entry The entry to modify
   */
  private static void flushFractionalConfigIntoEntry(
    LDAPReplicationDomain domain, Entry entry)
  private static void flushFractionalConfigIntoEntry(FractionalConfig
    localFractionalConfig, Entry entry)
  {
    if (domain.isFractional()) // Paranoia check
    if (localFractionalConfig.isFractional()) // Paranoia check
    {
      // Get the fractional configuration of the domain
      boolean fractionalExclusive = domain.isFractionalExclusive();
      boolean fractionalExclusive =
        localFractionalConfig.isFractionalExclusive();
      Map<String, List<String>> fractionalSpecificClassesAttributes =
        domain.getFractionalSpecificClassesAttributes();
        localFractionalConfig.getFractionalSpecificClassesAttributes();
      List<String> fractionalAllClassesAttributes =
        domain.getFractionalAllClassesAttributes();
        localFractionalConfig.getFractionalAllClassesAttributes();
      // Create attribute builder for the rigth fractional mode
      String fractAttribute = null;
opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -284,60 +284,8 @@
   * Fractional replication variables.
   */
  // Return type of the parseFractionalConfig method
  private static final int NOT_FRACTIONAL = 0;
  private static final int EXCLUSIVE_FRACTIONAL = 1;
  private static final int INCLUSIVE_FRACTIONAL = 2;
  /**
   * Tells if fractional replication is enabled or not (some fractional
   * constraints have been put in place). If this is true then
   * fractionalExclusive explains the configuration mode and either
   * fractionalSpecificClassesAttributes or fractionalAllClassesAttributes or
   * both should be filled with something.
   */
  private boolean fractional = false;
  /**
   * - If true, tells that the configured fractional replication is exclusive:
   * Every attributes contained in fractionalSpecificClassesAttributes and
   * fractionalAllClassesAttributes should be ignored when replaying operation
   * in local backend.
   * - If false, tells that the configured fractional replication is inclusive:
   * Only attributes contained in fractionalSpecificClassesAttributes and
   * fractionalAllClassesAttributes should be taken into account in local
   * backend.
   */
  private boolean fractionalExclusive = true;
  /**
   * Used in fractional replication: holds attributes of a specific object
   * class.
   * - key = object class (name or OID of the class)
   * - value = the attributes of that class that should be taken into account
   * (inclusive or exclusive fractional replication) (name or OID of the
   * attribute)
   * When an operation coming from the network is to be locally replayed, if the
   * concerned entry has an objectClass attribute equals to 'key':
   * - inclusive mode: only the attributes in 'value' will be added/deleted/
   * modified
   * - exclusive mode: the attributes in 'value' will not be added/deleted/
   * modified
   */
  private Map<String, List<String>> fractionalSpecificClassesAttributes =
    new HashMap<String, List<String>>();
  /**
   * Used in fractional replication: holds attributes of any object class. When
   * an operation coming from the network is to be locally replayed:
   * - inclusive mode: only attributes of the matching entry not present in
   * fractionalAllClassesAttributes will be added/deleted/modified
   * - exclusive mode: attributes of the matching entry present in
   * fractionalAllClassesAttributes will not be added/deleted/modified
   * The attributes may be in human readable form of OID form.
   */
  private List<String> fractionalAllClassesAttributes =
    new ArrayList<String>();
  // Holds the fractional configuration for this domain, if any.
  private FractionalConfig fractionalConfig = null;
  /**
   * The list of attributes that cannot be used in fractional replication
@@ -371,7 +319,7 @@
  private boolean followImport = true;
  /**
   * This is the message id to be used when an import is stopped with error by
   * The message id to be used when an import is stopped with error by
   * the fractional replication ldif import plugin.
   */
  private int importErrorMessageId = -1;
@@ -474,6 +422,7 @@
    readAssuredConfig(configuration, false);
    // Get fractional configuration
    fractionalConfig = new FractionalConfig(baseDn);
    readFractionalConfig(configuration, false);
    setGroupId((byte)configuration.getGroupId());
@@ -628,44 +577,6 @@
  }
  /**
   * Returns true if fractional replication is configured in this domain.
   * @return True if fractional replication is configured in this domain.
   */
  public boolean isFractional()
  {
    return fractional;
  }
  /**
   * Returns true if the fractional replication configuration is exclusive mode
   * in this domain, false if inclusive mode.
   * @return True if the fractional replication configuration is exclusive mode
   * in this domain, false if inclusive mode.
   */
  public boolean isFractionalExclusive()
  {
    return fractionalExclusive;
  }
  /**
   * Returns the fractional configuration for specific classes.
   * @return The fractional configuration for specific classes.
   */
  public Map<String, List<String>> getFractionalSpecificClassesAttributes()
  {
    return fractionalSpecificClassesAttributes;
  }
  /**
   * Returns the fractional configuration for all classes.
   * @return The fractional configuration for all classes.
   */
  public List<String> getFractionalAllClassesAttributes()
  {
    return fractionalAllClassesAttributes;
  }
  /**
   * Sets the error message id to be used when online import is stopped with
   * error by the fractional replication ldif import plugin.
   * @param importErrorMessageId The message to use.
@@ -695,34 +606,12 @@
  private void readFractionalConfig(ReplicationDomainCfg configuration,
    boolean allowReconnection)
  {
    boolean needReconnection = false;
    // Prepare fractional configuration variables to parse
    Iterator<String> exclIt = null;
    SortedSet<String> fractionalExclude = configuration.getFractionalExclude();
    if (fractionalExclude != null)
    {
      exclIt = fractionalExclude.iterator();
    }
    Iterator<String> inclIt = null;
    SortedSet<String> fractionalInclude = configuration.getFractionalInclude();
    if (fractionalInclude != null)
    {
      inclIt = fractionalInclude.iterator();
    }
    // Get potentially new fractional configuration
    Map<String, List<String>> newFractionalSpecificClassesAttributes =
    new HashMap<String, List<String>>();
    List<String> newFractionalAllClassesAttributes = new ArrayList<String>();
    int newFractionalMode = NOT_FRACTIONAL;
    // Read the configuration entry
    FractionalConfig newFractionalConfig = null;
    try
    {
      newFractionalMode = parseFractionalConfig(exclIt, inclIt,
      newFractionalSpecificClassesAttributes,
      newFractionalAllClassesAttributes);
      newFractionalConfig = FractionalConfig.toFractionalConfig(
        configuration);
    }
    catch(ConfigException e)
    {
@@ -738,14 +627,13 @@
    /**
     * Is there any change in fractional configuration ?
     */
    // Compute current configuration
    int fractionalMode = fractionalConfigToInt();
    boolean needReconnection = false;
     try
    {
      needReconnection = !isFractionalConfigEquivalent(fractionalMode,
        fractionalSpecificClassesAttributes, fractionalAllClassesAttributes,
        newFractionalMode, newFractionalSpecificClassesAttributes,
        newFractionalAllClassesAttributes);
      needReconnection = !FractionalConfig.
        isFractionalConfigEquivalent(fractionalConfig, newFractionalConfig);
    }
    catch  (ConfigException e)
    {
@@ -761,25 +649,28 @@
      disableService();
    // Set new configuration
    fractional = (newFractionalMode != NOT_FRACTIONAL);
    if (fractional)
    int newFractionalMode = newFractionalConfig.fractionalConfigToInt();
    fractionalConfig.setFractional(newFractionalMode !=
      FractionalConfig.NOT_FRACTIONAL);
    if (fractionalConfig.isFractional())
    {
      // Set new fractional configuration values
      if (newFractionalMode == EXCLUSIVE_FRACTIONAL)
        fractionalExclusive = true;
      if (newFractionalMode == FractionalConfig.EXCLUSIVE_FRACTIONAL)
        fractionalConfig.setFractionalExclusive(true);
      else
        fractionalExclusive = false;
      fractionalSpecificClassesAttributes =
        newFractionalSpecificClassesAttributes;
      fractionalAllClassesAttributes = newFractionalAllClassesAttributes;
        fractionalConfig.setFractionalExclusive(false);
      fractionalConfig.setFractionalSpecificClassesAttributes(
        newFractionalConfig.getFractionalSpecificClassesAttributes());
      fractionalConfig.setFractionalAllClassesAttributes(
        newFractionalConfig.fractionalAllClassesAttributes);
    } else
    {
      // Reset default values
      fractionalExclusive = true;
      fractionalSpecificClassesAttributes =
        new HashMap<String, List<String>>();
      fractionalAllClassesAttributes =
        new ArrayList<String>();
      fractionalConfig.setFractionalExclusive(true);
      fractionalConfig.setFractionalSpecificClassesAttributes(
        new HashMap<String, List<String>>());
      fractionalConfig.setFractionalAllClassesAttributes(
        new ArrayList<String>());
    }
    // Reconnect if required
@@ -873,7 +764,7 @@
      // The backend is probably empty: if there is some fractional
      // configuration in memory, we do not let the domain being connected,
      // otherwise, it's ok
      if (fractional)
      if (fractionalConfig.isFractional())
      {
        return false;
      }
@@ -916,13 +807,14 @@
    }
    // Compare backend and local fractional configuration
    return isFractionalConfigConsistent(exclIt, inclIt);
    return isFractionalConfigConsistent(fractionalConfig, exclIt, inclIt);
  }
  /**
   * Return true if the fractional configuration passed as fractional
   * configuration attribute values is equivalent to the fractional
   * configuration stored in the local variables.
   * @param fractionalConfig The local fractional configuration
   * @param exclIt Fractional exclude mode configuration attribute values to
   * analyze.
   * @param inclIt Fractional include mode configuration attribute values to
@@ -931,7 +823,8 @@
   * configuration attribute values is equivalent to the fractional
   * configuration stored in the local variables.
   */
  public boolean isFractionalConfigConsistent(Iterator<String> exclIt,
   static boolean isFractionalConfigConsistent(
    FractionalConfig fractionalConfig, Iterator<String> exclIt,
    Iterator<String> inclIt)
  {
    /*
@@ -943,68 +836,61 @@
      new HashMap<String, List<String>>();
    List<String> storedFractionalAllClassesAttributes = new ArrayList<String>();
    int storedFractionalMode = NOT_FRACTIONAL;
    int storedFractionalMode = FractionalConfig.NOT_FRACTIONAL;
    try
    {
      storedFractionalMode = parseFractionalConfig(exclIt, inclIt,
        storedFractionalSpecificClassesAttributes,
      storedFractionalMode = FractionalConfig.parseFractionalConfig(exclIt,
        inclIt, storedFractionalSpecificClassesAttributes,
        storedFractionalAllClassesAttributes);
    } catch (ConfigException e)
    {
      // Should not happen as configuration in domain root entry is flushed
      // from valid configuration in local variables
      Message message = NOTE_ERR_FRACTIONAL.get(baseDn.toString(),
        e.getLocalizedMessage());
      Message message = NOTE_ERR_FRACTIONAL.get(
        fractionalConfig.getBaseDn().toString(), e.getLocalizedMessage());
      logError(message);
      return false;
    }
    FractionalConfig storedFractionalConfig = new FractionalConfig(
      fractionalConfig.getBaseDn());
    storedFractionalConfig.setFractional(storedFractionalMode !=
      FractionalConfig.NOT_FRACTIONAL);
    // Set stored fractional configuration values
    if (storedFractionalConfig.isFractional())
    {
      if (storedFractionalMode == FractionalConfig.EXCLUSIVE_FRACTIONAL)
        storedFractionalConfig.setFractionalExclusive(true);
      else
        storedFractionalConfig.setFractionalExclusive(false);
    }
    storedFractionalConfig.setFractionalSpecificClassesAttributes(
      storedFractionalSpecificClassesAttributes);
    storedFractionalConfig.setFractionalAllClassesAttributes(
      storedFractionalAllClassesAttributes);
    /*
     * Compare configuration stored in passed fractional configuration
     * attributes with local variable one
     */
    // Compute current configuration from local variables
    int fractionalMode = fractionalConfigToInt();
    try
    {
      return isFractionalConfigEquivalent(fractionalMode,
        fractionalSpecificClassesAttributes, fractionalAllClassesAttributes,
        storedFractionalMode, storedFractionalSpecificClassesAttributes,
        storedFractionalAllClassesAttributes);
      return FractionalConfig.
        isFractionalConfigEquivalent(fractionalConfig, storedFractionalConfig);
    } catch (ConfigException e)
    {
      // Should not happen as configuration in domain root entry is flushed
      // from valid configuration in local variables so both should have already
      // been checked
      Message message = NOTE_ERR_FRACTIONAL.get(baseDn.toString(),
        e.getLocalizedMessage());
      Message message = NOTE_ERR_FRACTIONAL.get(
        fractionalConfig.getBaseDn().toString(), e.getLocalizedMessage());
      logError(message);
      return false;
    }
  }
  /**
   * Get an integer representation of the domain fractional configuration.
   * @return An integer representation of the domain fractional configuration.
   */
  public int fractionalConfigToInt()
  {
    int fractionalMode = -1;
    if (fractional)
    {
      if (fractionalExclusive)
        fractionalMode = EXCLUSIVE_FRACTIONAL;
      else
        fractionalMode = INCLUSIVE_FRACTIONAL;
    } else
    {
      fractionalMode = NOT_FRACTIONAL;
    }
    return fractionalMode;
  }
  /**
   * Utility class to have get a sting iterator from an AtributeValue iterator.
   * Assuming the attribute values are strings.
   */
@@ -1012,7 +898,6 @@
  {
    private Iterator<AttributeValue> attrValIt = null;
    /**
     * Creates a new AttributeValueStringIterator object.
     * @param attrValIt The underlying attribute iterator to use, assuming
@@ -1050,83 +935,6 @@
  }
  /**
   * Compare 2 fractional replication configurations and returns true if they
   * are equivalent.
   * @param mode1 Fractional mode 1
   * @param specificClassesAttributes1 Specific classes attributes 1
   * @param allClassesAttributes1 All classes attributes 1
   * @param mode2 Fractional mode 1
   * @param specificClassesAttributes2 Specific classes attributes 2
   * @param allClassesAttributes2 Fractional mode 2
   * @return True if both configurations are equivalent.
   * @throws ConfigException If some classes or attributes could not be
   * retrieved from the schema.
   */
  private static boolean isFractionalConfigEquivalent(int mode1,
    Map<String, List<String>> specificClassesAttributes1,
    List<String> allClassesAttributes1, int mode2,
    Map<String, List<String>> specificClassesAttributes2,
    List<String> allClassesAttributes2) throws ConfigException
  {
    // Compare modes
    if (mode1 != mode2)
      return false;
    // Compare all classes attributes
    if (!isAttributeListEquivalent(allClassesAttributes1,
      allClassesAttributes2))
            return false;
    // Compare specific classes attributes
    if (specificClassesAttributes1.size() != specificClassesAttributes2.size())
      return false;
    // Check consistency of specific classes attributes
    /*
     * For each class in specificClassesAttributes1, check that the attribute
     * list is equivalent to specificClassesAttributes2 attribute list
     */
    Schema schema = DirectoryServer.getSchema();
    for (String className1 : specificClassesAttributes1.keySet())
    {
      // Get class from specificClassesAttributes1
      ObjectClass objectClass1 = schema.getObjectClass(className1);
      if (objectClass1 == null)
      {
        throw new ConfigException(
          NOTE_ERR_FRACTIONAL_CONFIG_UNKNOWN_OBJECT_CLASS.get(className1));
      }
      // Look for matching one in specificClassesAttributes2
      boolean foundClass = false;
      for (String className2 : specificClassesAttributes2.keySet())
      {
        ObjectClass objectClass2 = schema.getObjectClass(className2);
        if (objectClass2 == null)
        {
          throw new ConfigException(
            NOTE_ERR_FRACTIONAL_CONFIG_UNKNOWN_OBJECT_CLASS.get(className2));
        }
        if (objectClass1.equals(objectClass2))
        {
          foundClass = true;
          // Now compare the 2 attribute lists
          List<String> attributes1 = specificClassesAttributes1.get(className1);
          List<String> attributes2 = specificClassesAttributes2.get(className2);
          if (!isAttributeListEquivalent(attributes1, attributes2))
            return false;
          break;
        }
      }
      // Found matching class ?
      if (!foundClass)
        return false;
    }
    return true;
  }
  /**
   * Compare 2 attribute lists and returns true if they are equivalent.
   * @param attributes1 First attribute list to compare.
   * @param attributes2 Second attribute list to compare.
@@ -1181,112 +989,6 @@
    return true;
  }
  /**
   * Parses a fractional replication configuration, filling the empty passed
   * variables and returning the used fractional mode. The 2 passed variables to
   * fill should be initialized (not null) and empty.
   * @param exclIt The list of fractional exclude configuration values (may be
   *               null)
   * @param inclIt The list of fractional include configuration values (may be
   *               null)
   * @param fractionalSpecificClassesAttributes An empty map to be filled with
   *        what is read from the fractional configuration properties.
   * @param fractionalAllClassesAttributes An empty list to be filled with what
   *        is read from the fractional configuration properties.
   * @return the fractional mode deduced from the passed configuration:
   *         not fractional, exclusive fractional or inclusive fractional modes
   */
  private static int parseFractionalConfig (
    Iterator<String> exclIt, Iterator<String> inclIt,
    Map<String, List<String>> fractionalSpecificClassesAttributes,
    List<String> fractionalAllClassesAttributes) throws ConfigException
  {
    int fractional_mode = NOT_FRACTIONAL;
    // Determine if fractional-exclude or fractional-include property is used
    // : only one of them is allowed
    Iterator<String> fracConfIt = null;
    // Deduce the wished fractional mode
    if ((exclIt != null) && exclIt.hasNext())
    {
      if ((inclIt != null) && inclIt.hasNext())
      {
        throw new ConfigException(NOTE_ERR_FRACTIONAL_CONFIG_BOTH_MODES.get());
      }
      else
      {
        fractional_mode = EXCLUSIVE_FRACTIONAL;
        fracConfIt = exclIt;
      }
    }
    else
    {
      if ((inclIt != null) && inclIt.hasNext())
      {
        fractional_mode = INCLUSIVE_FRACTIONAL;
        fracConfIt = inclIt;
      }
      else
      {
        return NOT_FRACTIONAL;
      }
    }
    while (fracConfIt.hasNext())
    {
      // Parse a value with the form class:attr1,attr2...
      // or *:attr1,attr2...
      String fractCfgStr = fracConfIt.next();
      StringTokenizer st = new StringTokenizer(fractCfgStr, ":");
      int nTokens = st.countTokens();
      if (nTokens < 2)
      {
        throw new ConfigException(NOTE_ERR_FRACTIONAL_CONFIG_WRONG_FORMAT.
          get(fractCfgStr));
      }
      // Get the class name
      String classNameLower = st.nextToken().toLowerCase();
      boolean allClasses = classNameLower.equals("*");
      // Get the attributes
      String attributes = st.nextToken();
      st = new StringTokenizer(attributes, ",");
      while (st.hasMoreTokens())
      {
        String attrNameLower = st.nextToken().toLowerCase();
        // Store attribute in the appropriate variable
        if (allClasses)
        {
          // Avoid duplicate attributes
          if (!fractionalAllClassesAttributes.contains(attrNameLower))
          {
            fractionalAllClassesAttributes.add(attrNameLower);
          }
        }
        else
        {
          List<String> attrList =
            fractionalSpecificClassesAttributes.get(classNameLower);
          if (attrList != null)
          {
            // Avoid duplicate attributes
            if (!attrList.contains(attrNameLower))
            {
              attrList.add(attrNameLower);
            }
          } else
          {
            attrList = new ArrayList<String>();
            attrList.add(attrNameLower);
            fractionalSpecificClassesAttributes.put(classNameLower, attrList);
          }
        }
      }
    }
    return fractional_mode;
  }
  /**
   * Check that the passed fractional configuration is acceptable
   * regarding configuration syntax, schema constraints...
@@ -1302,43 +1004,21 @@
     * Parse fractional configuration
     */
    // Prepare fractional configuration variables to parse
    Iterator<String> exclIt = null;
    SortedSet<String> fractionalExclude = configuration.getFractionalExclude();
    if (fractionalExclude != null)
    {
      exclIt = fractionalExclude.iterator();
    }
    // Read the configuration entry
    FractionalConfig newFractionalConfig = FractionalConfig.toFractionalConfig(
        configuration);
    Iterator<String> inclIt = null;
    SortedSet<String> fractionalInclude = configuration.getFractionalInclude();
    if (fractionalInclude != null)
    if (!newFractionalConfig.isFractional())
    {
      inclIt = fractionalInclude.iterator();
        // Nothing to check
        return;
    }
    // Prepare variables to be filled with config
    Map<String, List<String>> newFractionalSpecificClassesAttributes =
    new HashMap<String, List<String>>();
    List<String> newFractionalAllClassesAttributes = new ArrayList<String>();
    int fractionalMode = parseFractionalConfig(exclIt, inclIt,
      newFractionalSpecificClassesAttributes,
      newFractionalAllClassesAttributes);
    switch (fractionalMode)
    {
      case NOT_FRACTIONAL:
        // Nothing to check
        return;
      case EXCLUSIVE_FRACTIONAL:
      case INCLUSIVE_FRACTIONAL:
        // Ok, checking done out of the switch statement
        break;
      default:
      // Should not happen
        return;
    }
      newFractionalConfig.getFractionalSpecificClassesAttributes();
    List<String> newFractionalAllClassesAttributes =
      newFractionalConfig.getFractionalAllClassesAttributes();
    /*
     * Check attributes consistency : we only allow to filter MAY (optional)
@@ -1348,6 +1028,7 @@
    // Check consistency of specific classes attributes
    Schema schema = DirectoryServer.getSchema();
    int fractionalMode = newFractionalConfig.fractionalConfigToInt();
    for (String className : newFractionalSpecificClassesAttributes.keySet())
    {
      // Does the class exist ?
@@ -1381,7 +1062,7 @@
          // No more checking for the extensibleObject class
          if (!isExtensibleObjectClass)
          {
            if (fractionalMode == EXCLUSIVE_FRACTIONAL)
            if (fractionalMode == FractionalConfig.EXCLUSIVE_FRACTIONAL)
            {
              // Exclusive mode : the attribute must be optional
              if (!fractionalClass.isOptional(attributeType))
@@ -1455,7 +1136,7 @@
  public boolean fractionalFilterOperation(
    PreOperationAddOperation addOperation, boolean performFiltering)
  {
    return fractionalRemoveAttributesFromEntry(
    return fractionalRemoveAttributesFromEntry(fractionalConfig,
      addOperation.getEntryDN().getRDN(), addOperation.getObjectClasses(),
      addOperation.getUserAttributes(), performFiltering);
  }
@@ -1494,9 +1175,10 @@
    Entry concernedEntry = modifyDNOperation.getOriginalEntry();
    List<String> fractionalConcernedAttributes =
      createFractionalConcernedAttrList(
      createFractionalConcernedAttrList(fractionalConfig,
      concernedEntry.getObjectClasses().keySet());
    boolean fractionalExclusive = fractionalConfig.isFractionalExclusive();
    if ( fractionalExclusive && (fractionalConcernedAttributes.size() == 0) )
      // No attributes to filter
      return false;
@@ -1551,10 +1233,11 @@
  }
  /**
   * Remove attributes from an entry, according to the current fractional
   * Remove attributes from an entry, according to the passed fractional
   * configuration. The entry is represented by the 2 passed parameters.
   * The attributes to be removed are removed using the remove method on the
   * passed iterator for the attributes in the entry.
   * @param fractionalConfig The fractional configuration to use
   * @param entryRdn The rdn of the entry to add
   * @param classes The object classes representing the entry to modify
   * @param attributesMap The map of attributes/values to be potentially removed
@@ -1565,9 +1248,10 @@
   * @return true if the operation contains some attributes subject to filtering
   * by the fractional configuration
   */
  public boolean fractionalRemoveAttributesFromEntry(RDN entryRdn,
    Map<ObjectClass,String> classes, Map<AttributeType, List<Attribute>>
    attributesMap, boolean performFiltering)
   static boolean fractionalRemoveAttributesFromEntry(
    FractionalConfig fractionalConfig, RDN entryRdn,
    Map<ObjectClass,String> classes, Map<AttributeType,
    List<Attribute>> attributesMap, boolean performFiltering)
  {
    boolean hasSomeAttributesToFilter = false;
    /*
@@ -1576,7 +1260,8 @@
     */
    List<String> fractionalConcernedAttributes =
      createFractionalConcernedAttrList(classes.keySet());
      createFractionalConcernedAttrList(fractionalConfig, classes.keySet());
    boolean fractionalExclusive = fractionalConfig.isFractionalExclusive();
    if ( fractionalExclusive && (fractionalConcernedAttributes.size() == 0) )
      return false; // No attributes to filter
@@ -1724,13 +1409,14 @@
  /**
   * Prepares a list of attributes of interest for the fractional feature.
   * @param fractionalConfig The fractional configuration to use
   * @param entryObjectClasses The object classes of an entry on which an
   * operation is going to be performed.
   * @return The list of attributes of the entry to be excluded/included
   * when the operation will be performed.
   */
  private List<String> createFractionalConcernedAttrList(
    Set<ObjectClass> entryObjectClasses)
  private static List<String> createFractionalConcernedAttrList(
    FractionalConfig fractionalConfig, Set<ObjectClass> entryObjectClasses)
  {
    /*
     * Is the concerned entry of a type concerned by fractional replication
@@ -1742,6 +1428,11 @@
    List<String> fractionalConcernedAttributes = new ArrayList<String>();
    // Get object classes the entry matches
    List<String> fractionalAllClassesAttributes =
      fractionalConfig.getFractionalAllClassesAttributes();
    Map<String, List<String>> fractionalSpecificClassesAttributes =
      fractionalConfig.getFractionalSpecificClassesAttributes();
    Set<String> fractionalClasses =
        fractionalSpecificClassesAttributes.keySet();
    for (ObjectClass entryObjectClass : entryObjectClasses)
@@ -1803,8 +1494,9 @@
    Entry modifiedEntry = modifyOperation.getCurrentEntry();
    List<String> fractionalConcernedAttributes =
      createFractionalConcernedAttrList(
      createFractionalConcernedAttrList(fractionalConfig,
      modifiedEntry.getObjectClasses().keySet());
    boolean fractionalExclusive = fractionalConfig.isFractionalExclusive();
    if ( fractionalExclusive && (fractionalConcernedAttributes.size() == 0) )
      // No attributes to filter
      return FRACTIONAL_HAS_NO_FRACTIONAL_FILTERED_ATTRIBUTES;
@@ -1959,7 +1651,7 @@
  protected void initializeRemote(short target, short requestorID,
    Task initTask) throws DirectoryException
  {
    if ((target == RoutableMsg.ALL_SERVERS) && fractional)
    if ((target == RoutableMsg.ALL_SERVERS) && fractionalConfig.isFractional())
    {
      Message msg = NOTE_ERR_FRACTIONAL_FORBIDDEN_FULL_UPDATE_FRACTIONAL.get(
            baseDn.toString(), Short.toString(getServerId()));
@@ -2074,7 +1766,7 @@
          ResultCode.UNWILLING_TO_PERFORM, msg);
    }
    if (fractional)
    if (fractionalConfig.isFractional())
    {
      if (addOperation.isSynchronizationOperation())
      {
@@ -2212,7 +1904,7 @@
          ResultCode.UNWILLING_TO_PERFORM, msg);
    }
    if (fractional)
    if (fractionalConfig.isFractional())
    {
      if (modifyDNOperation.isSynchronizationOperation())
      {
@@ -2331,7 +2023,7 @@
          ResultCode.UNWILLING_TO_PERFORM, msg);
    }
    if (fractional)
    if (fractionalConfig.isFractional())
    {
      if  (modifyOperation.isSynchronizationOperation())
      {
@@ -4798,4 +4490,453 @@
          resultCode, message);
    }
  }
  /**
   * Gets the fractional configuration of this domain.
   * @return The fractional configuration of this domain.
   */
  FractionalConfig getFractionalConfig()
  {
    return fractionalConfig;
  }
  /**
   * This bean is a utility class used for holding the parsing
   * result of a fractional configuration. It also contains some facility
   * methods like fractional configuration comparison...
   */
  static class FractionalConfig
  {
    /**
     * Tells if fractional replication is enabled or not (some fractional
     * constraints have been put in place). If this is true then
     * fractionalExclusive explains the configuration mode and either
     * fractionalSpecificClassesAttributes or fractionalAllClassesAttributes or
     * both should be filled with something.
     */
    private boolean fractional = false;
    /**
     * - If true, tells that the configured fractional replication is exclusive:
     * Every attributes contained in fractionalSpecificClassesAttributes and
     * fractionalAllClassesAttributes should be ignored when replaying operation
     * in local backend.
     * - If false, tells that the configured fractional replication is
     * inclusive:
     * Only attributes contained in fractionalSpecificClassesAttributes and
     * fractionalAllClassesAttributes should be taken into account in local
     * backend.
     */
    private boolean fractionalExclusive = true;
    /**
     * Used in fractional replication: holds attributes of a specific object
     * class.
     * - key = object class (name or OID of the class)
     * - value = the attributes of that class that should be taken into account
     * (inclusive or exclusive fractional replication) (name or OID of the
     * attribute)
     * When an operation coming from the network is to be locally replayed, if
     * the concerned entry has an objectClass attribute equals to 'key':
     * - inclusive mode: only the attributes in 'value' will be added/deleted/
     * modified
     * - exclusive mode: the attributes in 'value' will not be added/deleted/
     * modified
     */
    private Map<String, List<String>> fractionalSpecificClassesAttributes =
      new HashMap<String, List<String>>();
    /**
     * Used in fractional replication: holds attributes of any object class.
     * When an operation coming from the network is to be locally replayed:
     * - inclusive mode: only attributes of the matching entry not present in
     * fractionalAllClassesAttributes will be added/deleted/modified
     * - exclusive mode: attributes of the matching entry present in
     * fractionalAllClassesAttributes will not be added/deleted/modified
     * The attributes may be in human readable form of OID form.
     */
    private List<String> fractionalAllClassesAttributes =
      new ArrayList<String>();
    /**
     * Base DN the fractional configuration is for.
     */
    private DN baseDn = null;
    /**
     * Constructs a new fractional configuration object.
     * @param baseDn The base dn the object is for.
     */
    FractionalConfig(DN baseDn)
    {
      this.baseDn = baseDn;
    }
    /**
     * Getter for fractional.
     * @return True if the configuration has fractional enabled
     */
    boolean isFractional()
    {
      return fractional;
    }
    /**
     * Set the fractional parameter.
     * @param fractional The fractional parameter
     */
    void setFractional(boolean fractional)
    {
      this.fractional = fractional;
    }
    /**
     * Getter for fractionalExclusive.
     * @return True if the configuration has fractional exclusive enabled
     */
    boolean isFractionalExclusive()
    {
      return fractionalExclusive;
    }
    /**
     * Set the fractionalExclusive parameter.
     * @param fractionalExclusive The fractionalExclusive parameter
     */
    void setFractionalExclusive(boolean fractionalExclusive)
    {
      this.fractionalExclusive = fractionalExclusive;
    }
    /**
     * Getter for fractionalSpecificClassesAttributes attribute.
     * @return The fractionalSpecificClassesAttributes attribute.
     */
    Map<String, List<String>> getFractionalSpecificClassesAttributes()
    {
      return fractionalSpecificClassesAttributes;
    }
    /**
     * Set the fractionalSpecificClassesAttributes parameter.
     * @param fractionalSpecificClassesAttributes The
     * fractionalSpecificClassesAttributes parameter to set.
     */
    void setFractionalSpecificClassesAttributes(Map<String,
      List<String>> fractionalSpecificClassesAttributes)
    {
      this.fractionalSpecificClassesAttributes =
        fractionalSpecificClassesAttributes;
    }
    /**
     * Getter for fractionalSpecificClassesAttributes attribute.
     * @return The fractionalSpecificClassesAttributes attribute.
     */
    List<String> getFractionalAllClassesAttributes()
    {
      return fractionalAllClassesAttributes;
    }
    /**
     * Set the fractionalAllClassesAttributes parameter.
     * @param fractionalAllClassesAttributes The
     * fractionalSpecificClassesAttributes parameter to set.
     */
    void setFractionalAllClassesAttributes(
      List<String> fractionalAllClassesAttributes)
    {
      this.fractionalAllClassesAttributes = fractionalAllClassesAttributes;
    }
    /**
     * Getter for the base baseDn.
     * @return The baseDn attribute.
     */
    DN getBaseDn()
    {
      return baseDn;
    }
    /**
     * Extract the fractional configuration from the passed domain configuration
     * entry.
     * @param configuration The configuration object
     * @return The fractional replication configuration.
     * @throws ConfigException If an error occurred.
     */
    static FractionalConfig toFractionalConfig(
      ReplicationDomainCfg configuration) throws ConfigException
    {
      // Prepare fractional configuration variables to parse
      Iterator<String> exclIt = null;
      SortedSet<String> fractionalExclude =
        configuration.getFractionalExclude();
      if (fractionalExclude != null)
      {
        exclIt = fractionalExclude.iterator();
      }
      Iterator<String> inclIt = null;
      SortedSet<String> fractionalInclude =
        configuration.getFractionalInclude();
      if (fractionalInclude != null)
      {
        inclIt = fractionalInclude.iterator();
      }
      // Get potentially new fractional configuration
      Map<String, List<String>> newFractionalSpecificClassesAttributes =
        new HashMap<String, List<String>>();
      List<String> newFractionalAllClassesAttributes = new ArrayList<String>();
      int newFractionalMode = parseFractionalConfig(exclIt, inclIt,
        newFractionalSpecificClassesAttributes,
        newFractionalAllClassesAttributes);
      // Create matching parsed config object
      FractionalConfig result = new FractionalConfig(configuration.getBaseDN());
      switch (newFractionalMode)
      {
        case NOT_FRACTIONAL:
          result.setFractional(false);
          result.setFractionalExclusive(true);
          break;
        case EXCLUSIVE_FRACTIONAL:
        case INCLUSIVE_FRACTIONAL:
          result.setFractional(true);
          if (newFractionalMode == EXCLUSIVE_FRACTIONAL)
            result.setFractionalExclusive(true);
          else
            result.setFractionalExclusive(false);
          break;
      }
      result.setFractionalSpecificClassesAttributes(
        newFractionalSpecificClassesAttributes);
      result.setFractionalAllClassesAttributes(
        newFractionalAllClassesAttributes);
      return result;
    }
    /**
     * Parses a fractional replication configuration, filling the empty passed
     * variables and returning the used fractional mode. The 2 passed variables
     * to fill should be initialized (not null) and empty.
     * @param exclIt The list of fractional exclude configuration values (may be
     *               null)
     * @param inclIt The list of fractional include configuration values (may be
     *               null)
     * @param fractionalSpecificClassesAttributes An empty map to be filled with
     *        what is read from the fractional configuration properties.
     * @param fractionalAllClassesAttributes An empty list to be filled with
     *        what is read from the fractional configuration properties.
     * @return the fractional mode deduced from the passed configuration:
     *         not fractional, exclusive fractional or inclusive fractional
     *         modes
     */
     private static int parseFractionalConfig (
      Iterator<String> exclIt, Iterator<String> inclIt,
      Map<String, List<String>> fractionalSpecificClassesAttributes,
      List<String> fractionalAllClassesAttributes) throws ConfigException
    {
      int fractional_mode = NOT_FRACTIONAL;
      // Determine if fractional-exclude or fractional-include property is used
      // : only one of them is allowed
      Iterator<String> fracConfIt = null;
      // Deduce the wished fractional mode
      if ((exclIt != null) && exclIt.hasNext())
      {
        if ((inclIt != null) && inclIt.hasNext())
        {
          throw new ConfigException(
            NOTE_ERR_FRACTIONAL_CONFIG_BOTH_MODES.get());
        }
        else
        {
          fractional_mode = EXCLUSIVE_FRACTIONAL;
          fracConfIt = exclIt;
        }
      }
      else
      {
        if ((inclIt != null) && inclIt.hasNext())
        {
          fractional_mode = INCLUSIVE_FRACTIONAL;
          fracConfIt = inclIt;
        }
        else
        {
          return NOT_FRACTIONAL;
        }
      }
      while (fracConfIt.hasNext())
      {
        // Parse a value with the form class:attr1,attr2...
        // or *:attr1,attr2...
        String fractCfgStr = fracConfIt.next();
        StringTokenizer st = new StringTokenizer(fractCfgStr, ":");
        int nTokens = st.countTokens();
        if (nTokens < 2)
        {
          throw new ConfigException(NOTE_ERR_FRACTIONAL_CONFIG_WRONG_FORMAT.
            get(fractCfgStr));
        }
        // Get the class name
        String classNameLower = st.nextToken().toLowerCase();
        boolean allClasses = classNameLower.equals("*");
        // Get the attributes
        String attributes = st.nextToken();
        st = new StringTokenizer(attributes, ",");
        while (st.hasMoreTokens())
        {
          String attrNameLower = st.nextToken().toLowerCase();
          // Store attribute in the appropriate variable
          if (allClasses)
          {
            // Avoid duplicate attributes
            if (!fractionalAllClassesAttributes.contains(attrNameLower))
            {
              fractionalAllClassesAttributes.add(attrNameLower);
            }
          }
          else
          {
            List<String> attrList =
              fractionalSpecificClassesAttributes.get(classNameLower);
            if (attrList != null)
            {
              // Avoid duplicate attributes
              if (!attrList.contains(attrNameLower))
              {
                attrList.add(attrNameLower);
              }
            } else
            {
              attrList = new ArrayList<String>();
              attrList.add(attrNameLower);
              fractionalSpecificClassesAttributes.put(classNameLower, attrList);
            }
          }
        }
      }
      return fractional_mode;
    }
    // Return type of the parseFractionalConfig method
    static final int NOT_FRACTIONAL = 0;
    static final int EXCLUSIVE_FRACTIONAL = 1;
    static final int INCLUSIVE_FRACTIONAL = 2;
    /**
     * Get an integer representation of the domain fractional configuration.
     * @return An integer representation of the domain fractional configuration.
     */
    int fractionalConfigToInt()
    {
      int fractionalMode = -1;
      if (fractional)
      {
        if (fractionalExclusive)
          fractionalMode = EXCLUSIVE_FRACTIONAL;
        else
          fractionalMode = INCLUSIVE_FRACTIONAL;
      } else
      {
        fractionalMode = NOT_FRACTIONAL;
      }
      return fractionalMode;
    }
    /**
     * Compare 2 fractional replication configurations and returns true if they
     * are equivalent.
     * @param fractionalConfig1 First fractional configuration
     * @param fractionalConfig2 Second fractional configuration
     * @return True if both configurations are equivalent.
     * @throws ConfigException If some classes or attributes could not be
     * retrieved from the schema.
     */
     static boolean isFractionalConfigEquivalent(
      FractionalConfig fractionalConfig1, FractionalConfig fractionalConfig2)
      throws ConfigException
    {
      // Comapre base DNs just to be consistent
      if (!fractionalConfig1.getBaseDn().equals(fractionalConfig2.getBaseDn()))
        return false;
      // Compare modes
      if ( (fractionalConfig1.isFractional() !=
        fractionalConfig2.isFractional()) ||
        (fractionalConfig1.isFractionalExclusive() !=
        fractionalConfig2.isFractionalExclusive()) )
        return false;
      // Compare all classes attributes
      List<String> allClassesAttributes1 =
        fractionalConfig1.getFractionalAllClassesAttributes();
      List<String> allClassesAttributes2 =
        fractionalConfig2.getFractionalAllClassesAttributes();
      if (!isAttributeListEquivalent(allClassesAttributes1,
        allClassesAttributes2))
              return false;
      // Compare specific classes attributes
      Map<String, List<String>> specificClassesAttributes1 =
        fractionalConfig1.getFractionalSpecificClassesAttributes();
      Map<String, List<String>> specificClassesAttributes2 =
        fractionalConfig2.getFractionalSpecificClassesAttributes();
      if (specificClassesAttributes1.size() !=
        specificClassesAttributes2.size())
        return false;
      // Check consistency of specific classes attributes
      /*
       * For each class in specificClassesAttributes1, check that the attribute
       * list is equivalent to specificClassesAttributes2 attribute list
       */
      Schema schema = DirectoryServer.getSchema();
      for (String className1 : specificClassesAttributes1.keySet())
      {
        // Get class from specificClassesAttributes1
        ObjectClass objectClass1 = schema.getObjectClass(className1);
        if (objectClass1 == null)
        {
          throw new ConfigException(
            NOTE_ERR_FRACTIONAL_CONFIG_UNKNOWN_OBJECT_CLASS.get(className1));
        }
        // Look for matching one in specificClassesAttributes2
        boolean foundClass = false;
        for (String className2 : specificClassesAttributes2.keySet())
        {
          ObjectClass objectClass2 = schema.getObjectClass(className2);
          if (objectClass2 == null)
          {
            throw new ConfigException(
              NOTE_ERR_FRACTIONAL_CONFIG_UNKNOWN_OBJECT_CLASS.get(className2));
          }
          if (objectClass1.equals(objectClass2))
          {
            foundClass = true;
            // Now compare the 2 attribute lists
            List<String> attributes1 =
              specificClassesAttributes1.get(className1);
            List<String> attributes2 =
              specificClassesAttributes2.get(className2);
            if (!isAttributeListEquivalent(attributes1, attributes2))
              return false;
            break;
          }
        }
        // Found matching class ?
        if (!foundClass)
          return false;
      }
      return true;
    }
  }
}
opends/src/server/org/opends/server/util/LDIFReader.java
@@ -1477,6 +1477,8 @@
   */
  public void close()
  {
    // Inform LDIF import plugins that an import session is ending
    pluginConfigManager.invokeLDIFImportEndPlugins(importConfig);
    importConfig.close();
  }
opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server;
@@ -1101,6 +1101,7 @@
      entries.add(entry);
    }
    reader.close();
    return entries;
  }
opends/tests/unit-tests-testng/src/server/org/opends/server/api/plugin/DirectoryServerPluginTestCase.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.api.plugin;
@@ -225,6 +225,12 @@
    expectedPublicMethods.add(sigList);
    sigList = new LinkedList<String>();
    sigList.add("doLDIFImportEnd");
    sigList.add("void");
    sigList.add("org.opends.server.types.LDIFImportConfig");
    expectedPublicMethods.add(sigList);
    sigList = new LinkedList<String>();
    sigList.add("doLDIFExport");
    sigList.add("org.opends.server.api.plugin.PluginResult$ImportLDIF");
    sigList.add("org.opends.server.types.LDIFExportConfig");