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

Jean-Noel Rouvignac
20.29.2014 cf0ae342a174ec03d0cef89e2687cf5f2b57b15e
opendj3-server-dev/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -37,7 +37,6 @@
import java.util.zip.DataFormatException;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
@@ -79,6 +78,7 @@
import static org.forgerock.opendj.ldap.ResultCode.*;
import static org.opends.messages.ReplicationMessages.*;
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.protocols.internal.InternalClientConnection.*;
import static org.opends.server.replication.plugin.EntryHistorical.*;
import static org.opends.server.replication.protocol.OperationContext.*;
import static org.opends.server.replication.service.ReplicationMonitor.*;
@@ -175,12 +175,16 @@
   * push incoming update messages.
   */
  private final BlockingQueue<UpdateToReplay> updateToReplayQueue;
  /** The number of naming conflicts successfully resolved. */
  private final AtomicInteger numResolvedNamingConflicts = new AtomicInteger();
  /** The number of modify conflicts successfully resolved. */
  private final AtomicInteger numResolvedModifyConflicts = new AtomicInteger();
  /** The number of unresolved naming conflicts. */
  private final AtomicInteger numUnresolvedNamingConflicts =
    new AtomicInteger();
      new AtomicInteger();
  private final PersistentServerState state;
  private int numReplayedPostOpCalled = 0;
  /** The number of updates replayed successfully by the replication. */
  private final AtomicInteger numReplayedPostOpCalled = new AtomicInteger();
  private volatile boolean generationIdSavedStatus = false;
@@ -205,12 +209,9 @@
   * not updated too early.
   */
  private final RemotePendingChanges remotePendingChanges;
  private final InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
  private boolean solveConflictFlag = true;
  private final InternalClientConnection conn = getRootConnection();
  private volatile boolean shutdown = false;
  private volatile boolean disabled = false;
  private volatile boolean stateSavingDisabled = false;
@@ -241,13 +242,13 @@
   * The attribute name used to store the fractional include configuration in
   * the backend.
   */
  public static final String REPLICATION_FRACTIONAL_INCLUDE =
  static final String REPLICATION_FRACTIONAL_INCLUDE =
    "ds-sync-fractional-include";
  /**
   * The attribute name used to store the fractional exclude configuration in
   * the backend.
   */
  public static final String REPLICATION_FRACTIONAL_EXCLUDE =
  static final String REPLICATION_FRACTIONAL_EXCLUDE =
    "ds-sync-fractional-exclude";
  /**
@@ -289,11 +290,11 @@
  /**
   * LocalizableMessage type for ERR_FULL_UPDATE_IMPORT_FRACTIONAL_BAD_REMOTE.
   */
  public static final int IMPORT_ERROR_MESSAGE_BAD_REMOTE = 1;
  static final int IMPORT_ERROR_MESSAGE_BAD_REMOTE = 1;
  /**
   * LocalizableMessage type for ERR_FULL_UPDATE_IMPORT_FRACTIONAL_REMOTE_IS_FRACTIONAL.
   */
  public static final int IMPORT_ERROR_MESSAGE_REMOTE_IS_FRACTIONAL = 2;
  static final int IMPORT_ERROR_MESSAGE_REMOTE_IS_FRACTIONAL = 2;
  /*
   * Definitions for the return codes of the
@@ -455,7 +456,7 @@
   * @param updateToReplayQueue The queue for update messages to replay.
   * @throws ConfigException In case of invalid configuration.
   */
  public LDAPReplicationDomain(ReplicationDomainCfg configuration,
  LDAPReplicationDomain(ReplicationDomainCfg configuration,
      BlockingQueue<UpdateToReplay> updateToReplayQueue) throws ConfigException
  {
    super(configuration, -1);
@@ -623,7 +624,9 @@
    // Reconnect if required
    if (needRestart)
    {
      enableService();
    }
  }
  /**
@@ -635,9 +638,11 @@
  {
    // Read config stored in domain root entry
    if (logger.isTraceEnabled())
    {
      logger.trace(
          "Attempt to read the potential fractional config in domain root "
              + "entry " + getBaseDNString());
    }
    LDAPFilter filter;
    try
@@ -650,11 +655,11 @@
    }
    // Search the domain root entry that is used to save the generation id
    ByteString asn1BaseDn = ByteString.valueOf(getBaseDNString());
    Set<String> attributes = new LinkedHashSet<String>(3);
    attributes.add(REPLICATION_GENERATION_ID);
    attributes.add(REPLICATION_FRACTIONAL_EXCLUDE);
    attributes.add(REPLICATION_FRACTIONAL_INCLUDE);
    final ByteString asn1BaseDn = ByteString.valueOf(getBaseDNString());
    final Set<String> attributes = newSet(
        REPLICATION_GENERATION_ID,
        REPLICATION_FRACTIONAL_EXCLUDE,
        REPLICATION_FRACTIONAL_INCLUDE);
    InternalSearchOperation search = conn.processSearch(asn1BaseDn,
      SearchScope.BASE_OBJECT,
      DereferenceAliasesPolicy.ALWAYS, 0, 0, false,
@@ -816,7 +821,7 @@
   * Utility class to have get a string iterator from an AtributeValue iterator.
   * Assuming the attribute values are strings.
   */
  public static class AttributeValueStringIterator implements Iterator<String>
  static class AttributeValueStringIterator implements Iterator<String>
  {
    private Iterator<ByteString> attrValIt;
@@ -825,7 +830,7 @@
     * @param attrValIt The underlying attribute iterator to use, assuming
     * internal values are strings.
     */
    public AttributeValueStringIterator(Iterator<ByteString> attrValIt)
    AttributeValueStringIterator(Iterator<ByteString> attrValIt)
    {
      this.attrValIt = attrValIt;
    }
@@ -870,7 +875,9 @@
  {
    // Compare all classes attributes
    if (attributes1.size() != attributes2.size())
    {
      return false;
    }
    // Check consistency of all classes attributes
    Schema schema = DirectoryServer.getSchema();
@@ -905,7 +912,9 @@
      }
      // Found matching attribute ?
      if (!foundAttribute)
      {
        return false;
      }
    }
    return true;
@@ -932,8 +941,8 @@
    if (!newFractionalConfig.isFractional())
    {
        // Nothing to check
        return;
      // Nothing to check
      return;
    }
    // Prepare variables to be filled with config
@@ -1091,8 +1100,10 @@
    boolean fractionalExclusive = fractionalConfig.isFractionalExclusive();
    if (fractionalExclusive && fractionalConcernedAttributes.isEmpty())
    {
      // No attributes to filter
      return false;
    }
    /*
     * Analyze the old and new rdn to see if they are some attributes to be
@@ -1112,21 +1123,10 @@
    for (int i=0 ; i<rdn.getNumValues() ; i++)
    {
      AttributeType attributeType = rdn.getAttributeType(i);
      boolean found = false;
      // Is it present in the fractional attributes established list ?
      for (String attrTypeStr : fractionalConcernedAttributes)
      {
        AttributeType attributeTypeFromList =
        DirectoryServer.getAttributeType(attrTypeStr);
        if (attributeTypeFromList.equals(attributeType))
        {
          found = true;
          break;
        }
      }
      boolean attributeToBeFiltered = (fractionalExclusive && found)
          || (!fractionalExclusive && !found);
      if (attributeToBeFiltered
      boolean foundAttribute =
          exists(fractionalConcernedAttributes, attributeType);
      if (canRemoveAttribute(fractionalExclusive, foundAttribute)
          && !newRdn.hasAttributeType(attributeType)
          && !modifyDNOperation.deleteOldRDN())
      {
@@ -1146,6 +1146,18 @@
    return inconsistentOperation;
  }
  private boolean exists(Set<String> attrNames, AttributeType attrTypeToFind)
  {
    for (String attrName : attrNames)
    {
      if (DirectoryServer.getAttributeType(attrName).equals(attrTypeToFind))
      {
        return true;
      }
    }
    return false;
  }
  /**
   * Remove attributes from an entry, according to the passed fractional
   * configuration. The entry is represented by the 2 passed parameters.
@@ -1177,7 +1189,9 @@
      createFractionalConcernedAttrList(fractionalConfig, classes.keySet());
    boolean fractionalExclusive = fractionalConfig.isFractionalExclusive();
    if (fractionalExclusive && fractionalConcernedAttributes.isEmpty())
    {
      return false; // No attributes to filter
    }
    // Prepare list of object classes of the added entry
    Set<ObjectClass> entryClasses = classes.keySet();
@@ -1189,12 +1203,12 @@
     * - include mode : remove any attribute that is not in
     * fractionalConcernedAttributes
     */
    Iterator<AttributeType> attributeTypes = attributesMap.keySet().iterator();
    List<List<Attribute>> newRdnAttrLists = new ArrayList<List<Attribute>>();
    List<AttributeType> rdnAttrTypes = new ArrayList<AttributeType>();
    while (attributeTypes.hasNext())
    final Set<AttributeType> attrTypes = attributesMap.keySet();
    for (Iterator<AttributeType> iter = attrTypes.iterator(); iter.hasNext();)
    {
      AttributeType attributeType = attributeTypes.next();
      AttributeType attributeType = iter.next();
      // Only optional attributes may be removed
      if (isMandatoryAttribute(entryClasses, attributeType)
@@ -1253,11 +1267,8 @@
          // find the attribute/value pair matching the pair in the RDN
        {
          // Construct and store new attribute list
          AttributeBuilder attrBuilder = new AttributeBuilder(attributeType);
          attrBuilder.add(sameAttrValue);
          List<Attribute> newRdnAttrList = new ArrayList<Attribute>();
          newRdnAttrList.add(attrBuilder.toAttribute());
          newRdnAttrLists.add(newRdnAttrList);
          newRdnAttrLists.add(
              newList(Attributes.create(attributeType, sameAttrValue)));
          /*
          Store matching attribute type
          The mapping will be done using object from rdnAttrTypes as key
@@ -1270,7 +1281,7 @@
      else
      {
        // Found an attribute to remove, remove it from the list.
        attributeTypes.remove();
        iter.remove();
        hasSomeAttributesToFilter = true;
      }
    }
@@ -1283,6 +1294,23 @@
    return hasSomeAttributesToFilter;
  }
  private static <T> ArrayList<T> newList(T elem)
  {
    final ArrayList<T> list = new ArrayList<T>(1);
    list.add(elem);
    return list;
  }
  private static <T> Set<T> newSet(T... elems)
  {
    final Set<T> list = new LinkedHashSet<T>(elems.length);
    for (T elem : elems)
    {
      list.add(elem);
    }
    return list;
  }
   private static boolean isMandatoryAttribute(Set<ObjectClass> entryClasses,
       AttributeType attributeType)
   {
@@ -1315,19 +1343,21 @@
    // Now remove the attribute or modification if:
    // - exclusive mode and attribute is in configuration
    // - inclusive mode and attribute is not in configuration
    return canRemoveAttribute(fractionalExclusive, foundAttribute);
  }
  private static boolean canRemoveAttribute(boolean fractionalExclusive,
      boolean foundAttribute)
  {
    return (foundAttribute && fractionalExclusive)
        || (!foundAttribute && !fractionalExclusive);
  }
  private static boolean contains(Set<String> fractionalConcernedAttributes,
      String attributeName, String attributeOid)
  private static boolean contains(Set<String> attrNames, String attrName,
      String attrOID)
  {
    final boolean foundAttribute =
        attributeName != null
            && fractionalConcernedAttributes.contains(attributeName
                .toLowerCase());
    return foundAttribute
        || fractionalConcernedAttributes.contains(attributeOid);
    return attrNames.contains(attrOID)
        || (attrName != null && attrNames.contains(attrName.toLowerCase()));
  }
  /**
@@ -1404,8 +1434,10 @@
      modifiedEntry.getObjectClasses().keySet());
    boolean fractionalExclusive = fractionalConfig.isFractionalExclusive();
    if (fractionalExclusive && fractionalConcernedAttributes.isEmpty())
    {
      // No attributes to filter
      return FRACTIONAL_HAS_NO_FRACTIONAL_FILTERED_ATTRIBUTES;
    }
    // Prepare list of object classes of the modified entry
    DN entryToModifyDn = modifyOperation.getEntryDN();
@@ -1558,8 +1590,8 @@
       * as it was in the original message.
       */
      String operationEntryUUID = ctx.getEntryUUID();
      String modifiedEntryUUID = EntryHistorical.getEntryUUID(deletedEntry);
      if (!operationEntryUUID.equals(modifiedEntryUUID))
      String deletedEntryUUID = getEntryUUID(deletedEntry);
      if (!operationEntryUUID.equals(deletedEntryUUID))
      {
        /*
         * The changes entry is not the same entry as the one on
@@ -1581,7 +1613,7 @@
      // There is no replication context attached to the operation
      // so this is not a replication operation.
      CSN csn = generateCSN(deleteOperation);
      String modifiedEntryUUID = EntryHistorical.getEntryUUID(deletedEntry);
      String modifiedEntryUUID = getEntryUUID(deletedEntry);
      ctx = new DeleteContext(csn, modifiedEntryUUID);
      deleteOperation.setAttachment(SYNCHROCONTEXT, ctx);
@@ -1785,8 +1817,8 @@
       * Check that the modified entry has the same entryuuid
       * as was in the original message.
       */
      String modifiedEntryUUID =
        EntryHistorical.getEntryUUID(modifyDNOperation.getOriginalEntry());
      final String modifiedEntryUUID =
          getEntryUUID(modifyDNOperation.getOriginalEntry());
      if (!modifiedEntryUUID.equals(ctx.getEntryUUID()))
      {
        /*
@@ -1842,7 +1874,7 @@
      }
      Entry modifiedEntry = modifyDNOperation.getOriginalEntry();
      String modifiedEntryUUID = EntryHistorical.getEntryUUID(modifiedEntry);
      String modifiedEntryUUID = getEntryUUID(modifiedEntry);
      ctx = new ModifyDnContext(csn, modifiedEntryUUID, newParentId);
      modifyDNOperation.setAttachment(SYNCHROCONTEXT, ctx);
    }
@@ -1920,8 +1952,7 @@
      // - attach the context to the op
      CSN csn = generateCSN(modifyOperation);
      String modifiedEntryUUID = EntryHistorical.getEntryUUID(modifiedEntry);
      ctx = new ModifyContext(csn, modifiedEntryUUID);
      ctx = new ModifyContext(csn, getEntryUUID(modifiedEntry));
      modifyOperation.setAttachment(SYNCHROCONTEXT, ctx);
    }
@@ -1932,7 +1963,7 @@
      // - check if the entry has been renamed
      // - check for conflicts
      String modifiedEntryUUID = ctx.getEntryUUID();
      String currentEntryUUID = EntryHistorical.getEntryUUID(modifiedEntry);
      String currentEntryUUID = getEntryUUID(modifiedEntry);
      if (currentEntryUUID != null
          && !currentEntryUUID.equals(modifiedEntryUUID))
      {
@@ -1974,12 +2005,12 @@
   *
   * @param addOperation The Add Operation.
   */
  public void doPreOperation(PreOperationAddOperation addOperation)
  void doPreOperation(PreOperationAddOperation addOperation)
  {
    AddContext ctx = new AddContext(generateCSN(addOperation),
        EntryHistorical.getEntryUUID(addOperation),
    final CSN csn = generateCSN(addOperation);
    final String entryUUID = getEntryUUID(addOperation);
    final AddContext ctx = new AddContext(csn, entryUUID,
        findEntryUUID(addOperation.getEntryDN().getParentDNInSuffix()));
    addOperation.setAttachment(SYNCHROCONTEXT, ctx);
  }
@@ -1988,7 +2019,7 @@
   * Also update the list of pending changes and the server RUV
   * @param op the operation
   */
  public void synchronize(PostOperationOperation op)
  void synchronize(PostOperationOperation op)
  {
    ResultCode result = op.getResultCode();
    // Note that a failed non-replication operation might not have a change
@@ -2004,12 +2035,12 @@
    {
      if (op.isSynchronizationOperation())
      { // Replaying a sync operation
        numReplayedPostOpCalled++;
        numReplayedPostOpCalled.incrementAndGet();
        try
        {
          remotePendingChanges.commit(curCSN);
        }
        catch  (NoSuchElementException e)
        catch (NoSuchElementException e)
        {
          logger.error(ERR_OPERATION_NOT_FOUND_IN_PENDING, op, curCSN);
          return;
@@ -2188,8 +2219,7 @@
      boolean markConflict)
  {
    ModifyDNOperation newOp = new ModifyDNOperationBasis(
        conn, InternalClientConnection.nextOperationID(),
        InternalClientConnection.nextMessageID(), new ArrayList<Control>(0),
        conn, nextOperationID(), nextMessageID(), new ArrayList<Control>(0),
        targetDN, newRDN, false, parentDN);
    AttributeType attrType =
@@ -2219,32 +2249,9 @@
  }
  /**
   * Get the number of updates in the pending list.
   *
   * @return The number of updates in the pending list
   */
  private int getPendingUpdatesCount()
  {
    if (pendingChanges != null)
      return pendingChanges.size();
    return 0;
  }
  /**
   * get the number of updates replayed successfully by the replication.
   *
   * @return The number of updates replayed successfully
   */
  private int getNumReplayedPostOpCalled()
  {
    return numReplayedPostOpCalled;
  }
  /**
   * Delete this ReplicationDomain.
   */
  public void delete()
  void delete()
  {
    shutdown();
    removeECLDomainCfg();
@@ -2301,7 +2308,7 @@
   * @param shutdown
   *          whether the server initiated shutdown
   */
  public void replay(LDAPUpdateMsg msg, AtomicBoolean shutdown)
  void replay(LDAPUpdateMsg msg, AtomicBoolean shutdown)
  {
    // Try replay the operation, then flush (replaying) any pending operation
    // whose dependency has been replayed until no more left.
@@ -2461,6 +2468,7 @@
          LocalizableMessage message =
              ERR_EXCEPTION_REPLAYING_OPERATION.get(
                  stackTraceToSingleLineString(e), op);
          logger.error(message);
          replayErrorMsg = message.toString();
          updateError(csn);
        } else
@@ -2541,28 +2549,28 @@
  static String findEntryUUID(DN dn)
  {
    if (dn == null)
    {
      return null;
    }
    try
    {
      InternalClientConnection conn =
                InternalClientConnection.getRootConnection();
      Set<String> attrs = new LinkedHashSet<String>(1);
      attrs.add(ENTRYUUID_ATTRIBUTE_NAME);
      InternalSearchOperation search = conn.processSearch(dn,
            SearchScope.BASE_OBJECT, DereferenceAliasesPolicy.NEVER,
            0, 0, false,
            SearchFilter.createFilterFromString("(objectclass=*)"),
            attrs);
      final Set<String> attrs = newSet(ENTRYUUID_ATTRIBUTE_NAME);
      final InternalSearchOperation search = getRootConnection().processSearch(
          dn, SearchScope.BASE_OBJECT, DereferenceAliasesPolicy.NEVER,
          0, 0, false,
          SearchFilter.createFilterFromString("(objectclass=*)"),
          attrs);
      if (search.getResultCode() == ResultCode.SUCCESS)
      {
        List<SearchResultEntry> result = search.getSearchEntries();
        final List<SearchResultEntry> result = search.getSearchEntries();
        if (!result.isEmpty())
        {
          SearchResultEntry resultEntry = result.get(0);
          final SearchResultEntry resultEntry = result.get(0);
          if (resultEntry != null)
          {
            return EntryHistorical.getEntryUUID(resultEntry);
            return getEntryUUID(resultEntry);
          }
        }
      }
@@ -2589,10 +2597,10 @@
            SearchFilter.createFilterFromString("entryuuid="+uuid));
      if (search.getResultCode() == ResultCode.SUCCESS)
      {
        List<SearchResultEntry> result = search.getSearchEntries();
        if (!result.isEmpty())
        final List<SearchResultEntry> results = search.getSearchEntries();
        if (!results.isEmpty())
        {
          SearchResultEntry resultEntry = result.get(0);
          final SearchResultEntry resultEntry = results.get(0);
          if (resultEntry != null)
          {
            return resultEntry.getName();
@@ -2613,8 +2621,7 @@
   * @param msg The operation that triggered the conflict detection.
   * @return true if the process is completed, false if it must continue..
   */
  private boolean solveNamingConflict(ModifyOperation op,
      ModifyMsg msg)
  private boolean solveNamingConflict(ModifyOperation op, ModifyMsg msg)
  {
    ResultCode result = op.getResultCode();
    ModifyContext ctx = (ModifyContext) op.getAttachment(SYNCHROCONTEXT);
@@ -2702,8 +2709,7 @@
  * @param msg The operation that triggered the conflict detection.
  * @return true if the process is completed, false if it must continue..
  */
 private boolean solveNamingConflict(DeleteOperation op,
     LDAPUpdateMsg msg)
 private boolean solveNamingConflict(DeleteOperation op, LDAPUpdateMsg msg)
 {
   ResultCode result = op.getResultCode();
   DeleteContext ctx = (DeleteContext) op.getAttachment(SYNCHROCONTEXT);
@@ -2721,7 +2727,7 @@
        * The entry has already been deleted, either because this delete
        * has already been replayed or because another concurrent delete
        * has already done the job.
        * In any case, there is is nothing more to do.
        * In any case, there is nothing more to do.
        */
       numResolvedNamingConflicts.incrementAndGet();
       return true;
@@ -2763,7 +2769,7 @@
   }
 }
  /**
/**
 * Solve a conflict detected when replaying a Modify DN operation.
 *
 * @param op The operation that triggered the conflict detection.
@@ -2771,8 +2777,8 @@
 * @return true if the process is completed, false if it must continue.
 * @throws Exception When the operation is not valid.
 */
private boolean solveNamingConflict(ModifyDNOperation op,
    LDAPUpdateMsg msg) throws Exception
private boolean solveNamingConflict(ModifyDNOperation op, LDAPUpdateMsg msg)
    throws Exception
{
  ResultCode result = op.getResultCode();
  ModifyDnContext ctx = (ModifyDnContext) op.getAttachment(SYNCHROCONTEXT);
@@ -2891,8 +2897,8 @@
   * @return true if the process is completed, false if it must continue.
   * @throws Exception When the operation is not valid.
   */
  private boolean solveNamingConflict(AddOperation op,
      AddMsg msg) throws Exception
  private boolean solveNamingConflict(AddOperation op, AddMsg msg)
      throws Exception
  {
    ResultCode result = op.getResultCode();
    AddContext ctx = (AddContext) op.getAttachment(SYNCHROCONTEXT);
@@ -2999,9 +3005,8 @@
    // Find an rename child entries.
    try
    {
      Set<String> attrs = new LinkedHashSet<String>(1);
      attrs.add(EntryHistorical.ENTRYUUID_ATTRIBUTE_NAME);
      attrs.add(EntryHistorical.HISTORICAL_ATTRIBUTE_NAME);
      final Set<String> attrs =
          newSet(ENTRYUUID_ATTRIBUTE_NAME, HISTORICAL_ATTRIBUTE_NAME);
      InternalSearchOperation op =
          conn.processSearch(entryDN, SearchScope.SINGLE_LEVEL,
@@ -3022,34 +3027,22 @@
             * and keep the entry as a conflicting entry,
             */
            conflict = true;
            renameConflictEntry(conflictOp, entry.getName(),
                EntryHistorical.getEntryUUID(entry));
            renameConflictEntry(conflictOp, entry.getName(), getEntryUUID(entry));
          }
        }
      }
      else
      {
        // log error and information for the REPAIR tool.
        LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
        mb.append(ERR_CANNOT_RENAME_CONFLICT_ENTRY.get());
        mb.append(entryDN);
        mb.append(" ");
        mb.append(conflictOp);
        mb.append(" ");
        mb.append(op.getResultCode());
        logger.error(mb.toMessage());
        logger.error(ERR_CANNOT_RENAME_CONFLICT_ENTRY,
            entryDN, conflictOp, op.getResultCode());
      }
    } catch (DirectoryException e)
    }
    catch (DirectoryException e)
    {
      // log error and information for the REPAIR tool.
      LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
      mb.append(ERR_EXCEPTION_RENAME_CONFLICT_ENTRY.get());
      mb.append(entryDN);
      mb.append(" ");
      mb.append(conflictOp);
      mb.append(" ");
      mb.append(stackTraceToSingleLineString(e));
      logger.error(mb.toMessage());
      logger.error(ERR_EXCEPTION_RENAME_CONFLICT_ENTRY,
          entryDN, conflictOp, stackTraceToSingleLineString(e));
    }
    return conflict;
@@ -3077,14 +3070,8 @@
    if (newOp.getResultCode() != ResultCode.SUCCESS)
    {
      // log information for the repair tool.
      LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
      mb.append(ERR_CANNOT_RENAME_CONFLICT_ENTRY.get());
      mb.append(dn);
      mb.append(" ");
      mb.append(conflictOp);
      mb.append(" ");
      mb.append(newOp.getResultCode());
      logger.error(mb.toMessage());
      logger.error(ERR_CANNOT_RENAME_CONFLICT_ENTRY,
          dn, conflictOp, newOp.getResultCode());
    }
  }
@@ -3103,24 +3090,18 @@
    AttributeType attrType = DirectoryServer.getAttributeType(DS_SYNC_CONFLICT,
        true);
    Attribute attr = Attributes.create(attrType, conflictDN.toNormalizedString());
    List<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE, attr));
    List<Modification> mods =
        newList(new Modification(ModificationType.REPLACE, attr));
    ModifyOperation newOp = new ModifyOperationBasis(
          conn, InternalClientConnection.nextOperationID(),
          InternalClientConnection.nextMessageID(), new ArrayList<Control>(0),
          conn, nextOperationID(), nextMessageID(), new ArrayList<Control>(0),
          currentDN, mods);
    runAsSynchronizedOperation(newOp);
    if (newOp.getResultCode() != ResultCode.SUCCESS)
    {
      // Log information for the repair tool.
      LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
      mb.append(ERR_CANNOT_ADD_CONFLICT_ATTRIBUTE.get());
      mb.append(op);
      mb.append(" ");
      mb.append(newOp.getResultCode());
      logger.error(mb.toMessage());
      logger.error(ERR_CANNOT_ADD_CONFLICT_ATTRIBUTE, op, newOp.getResultCode());
    }
    // Generate an alert to let the administration know that some
@@ -3189,38 +3170,11 @@
  }
  /**
   * Get the number of modify conflicts successfully resolved.
   * @return The number of modify conflicts successfully resolved.
   */
  private int getNumResolvedModifyConflicts()
  {
    return numResolvedModifyConflicts.get();
  }
  /**
   * Get the number of naming conflicts successfully resolved.
   * @return The number of naming conflicts successfully resolved.
   */
  private int getNumResolvedNamingConflicts()
  {
    return numResolvedNamingConflicts.get();
  }
  /**
   * Get the number of unresolved conflicts.
   * @return The number of unresolved conflicts.
   */
  private int getNumUnresolvedNamingConflicts()
  {
    return numUnresolvedNamingConflicts.get();
  }
  /**
   * Check if the domain solve conflicts.
   *
   * @return a boolean indicating if the domain should solve conflicts.
   */
  public boolean solveConflict()
  boolean solveConflict()
  {
    return solveConflictFlag;
  }
@@ -3249,8 +3203,7 @@
   * and generationId.
   * @exception DirectoryException Thrown when an error occurs.
   */
  protected void loadDataState()
  throws DirectoryException
  private void loadDataState() throws DirectoryException
  {
    state.clearInMemory();
    state.loadState();
@@ -3298,11 +3251,11 @@
   */
  private long computeGenerationId() throws DirectoryException
  {
    long genId = exportBackend(null, true);
    final long genId = exportBackend(null, true);
    if (logger.isTraceEnabled())
    {
      logger.trace("Computed generationId: generationId=" + genId);
    }
    return genId;
  }
@@ -3318,23 +3271,18 @@
  private ResultCode runSaveGenerationId(DN entryDN, long generationId)
  {
    // The generationId is stored in the root entry of the domain.
    ByteString asn1BaseDn = ByteString.valueOf(entryDN.toString());
    final ByteString asn1BaseDn = ByteString.valueOf(entryDN.toString());
    final ArrayList<ByteString> values =
        newList(ByteString.valueOf(Long.toString(generationId)));
    ArrayList<ByteString> values = new ArrayList<ByteString>();
    values.add(ByteString.valueOf(Long.toString(generationId)));
    LDAPAttribute attr =
      new LDAPAttribute(REPLICATION_GENERATION_ID, values);
    LDAPAttribute attr = new LDAPAttribute(REPLICATION_GENERATION_ID, values);
    List<RawModification> mods = new ArrayList<RawModification>(1);
    mods.add(new LDAPModification(ModificationType.REPLACE, attr));
    ModifyOperation op = new ModifyOperationBasis(
          conn, InternalClientConnection.nextOperationID(),
          InternalClientConnection.nextMessageID(),
          new ArrayList<Control>(0), asn1BaseDn,
          mods);
          conn, nextOperationID(), nextMessageID(), new ArrayList<Control>(0),
          asn1BaseDn, mods);
    runAsSynchronizedOperation(op);
    return op.getResultCode();
  }
@@ -3343,10 +3291,9 @@
   * @param generationId The value of the generationId.
   * @return a ResultCode indicating if the method was successful.
   */
  public ResultCode saveGenerationId(long generationId)
  private ResultCode saveGenerationId(long generationId)
  {
    ResultCode result = runSaveGenerationId(getBaseDN(), generationId);
    if (result != ResultCode.SUCCESS)
    {
      generationIdSavedStatus = false;
@@ -3381,15 +3328,16 @@
  private long loadGenerationId() throws DirectoryException
  {
    if (logger.isTraceEnabled())
    {
      logger.trace("Attempt to read generation ID from DB "
          + getBaseDNString());
    }
    /*
     * Search the database entry that is used to periodically
     * save the generation id
     */
    final Set<String> attributes = new LinkedHashSet<String>(1);
    attributes.add(REPLICATION_GENERATION_ID);
    final Set<String> attributes = newSet(REPLICATION_GENERATION_ID);
    final String filter = "(objectclass=*)";
    InternalSearchOperation search = conn.processSearch(getBaseDNString(),
        SearchScope.BASE_OBJECT,
@@ -3457,15 +3405,19 @@
      saveGenerationId(aGenerationId);
      if (logger.isTraceEnabled())
      {
        logger.trace("Generation ID created for domain baseDN="
            + getBaseDNString() + " generationId=" + aGenerationId);
      }
    }
    else
    {
      generationIdSavedStatus = true;
      if (logger.isTraceEnabled())
      {
        logger.trace("Generation ID successfully read from domain baseDN="
            + getBaseDNString() + " generationId=" + aGenerationId);
      }
    }
    return aGenerationId;
  }
@@ -3567,10 +3519,8 @@
    }
    // baseDN branch is the only one included in the export
    List<DN> includeBranches = new ArrayList<DN>(1);
    includeBranches.add(getBaseDN());
    LDIFExportConfig exportConfig = new LDIFExportConfig(os);
    exportConfig.setIncludeBranches(includeBranches);
    exportConfig.setIncludeBranches(newList(getBaseDN()));
    // For the checksum computing mode, only consider the 'stable' attributes
    if (checksumOutput)
@@ -3598,8 +3548,7 @@
    }
    catch (DirectoryException de)
    {
      if (ros == null ||
          ros.getNumExportedEntries() < entryCount)
      if (ros == null || ros.getNumExportedEntries() < entryCount)
      {
        LocalizableMessage message = ERR_LDIFEXPORT_ERROR_DURING_EXPORT.get(de.getMessageObject());
        logger.error(message);
@@ -3699,9 +3648,7 @@
      }
      importConfig = new LDIFImportConfig(input);
      List<DN> includeBranches = new ArrayList<DN>();
      includeBranches.add(getBaseDN());
      importConfig.setIncludeBranches(includeBranches);
      importConfig.setIncludeBranches(newList(getBaseDN()));
      importConfig.setAppendToExistingData(false);
      importConfig.setSkipDNValidation(true);
      // We should not validate schema for replication
@@ -3765,7 +3712,9 @@
    }
    if (ieCtx.getException() != null)
    {
      throw ieCtx.getException();
    }
  }
  /**
@@ -3773,7 +3722,7 @@
   * @param backend The backend implied in the import.
   * @exception DirectoryException Thrown when an error occurs.
   */
  protected void closeBackendImport(Backend backend) throws DirectoryException
  private void closeBackendImport(Backend backend) throws DirectoryException
  {
    String lockFile = LockFileManager.getBackendLockFileName(backend);
    StringBuilder failureReason = new StringBuilder();
@@ -3859,14 +3808,11 @@
   * @param modifications
   *          The modification to push
   */
  public void synchronizeModifications(List<Modification> modifications)
  void synchronizeModifications(List<Modification> modifications)
  {
    ModifyOperation op = new ModifyOperationBasis(
                          InternalClientConnection.getRootConnection(),
                          InternalClientConnection.nextOperationID(),
                          InternalClientConnection.nextMessageID(),
                          null, DirectoryServer.getSchemaDN(),
                          modifications);
        conn, nextOperationID(), nextMessageID(), null,
        DirectoryServer.getSchemaDN(), modifications);
    LocalBackendModifyOperation localOp = new LocalBackendModifyOperation(op);
    CSN csn = generateCSN(localOp);
@@ -3886,8 +3832,8 @@
   *
   * @return true if the configuration is acceptable, false other wise.
   */
  public static boolean isConfigurationAcceptable(
      ReplicationDomainCfg configuration, List<LocalizableMessage> unacceptableReasons)
  static boolean isConfigurationAcceptable(ReplicationDomainCfg configuration,
      List<LocalizableMessage> unacceptableReasons)
  {
    // Check that there is not already a domain with the same DN
    final DN dn = configuration.getBaseDN();
@@ -4125,8 +4071,10 @@
        ECLWorkflowElement wfe = (ECLWorkflowElement)
        DirectoryServer.getWorkflowElement(
            ECLWorkflowElement.ECL_WORKFLOW_ELEMENT);
        if (wfe!=null)
        if (wfe != null)
        {
          wfe.getReplicationServer().enableECL();
        }
      }
      catch (DirectoryException de)
      {
@@ -4198,7 +4146,7 @@
   * @throws Exception
   *           if an Exception happens during the search.
   */
  public boolean buildAndPublishMissingChanges(CSN startCSN,
  boolean buildAndPublishMissingChanges(CSN startCSN,
      ReplicationBroker session, AtomicBoolean shutdown) throws Exception
  {
    // Trim the changes in replayOperations that are older than the startCSN.
@@ -4321,13 +4269,10 @@
      CSN fromCSN, CSN lastCSN, InternalSearchListener resultListener)
      throws Exception
  {
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    Integer serverId = fromCSN.getServerId();
    String maxValueForId;
    if (lastCSN == null)
    {
      final Integer serverId = fromCSN.getServerId();
      maxValueForId = "ffffffffffffffff" + String.format("%04x", serverId)
                      + "ffffffff";
    }
@@ -4340,7 +4285,7 @@
        "(&(" + HISTORICAL_ATTRIBUTE_NAME + ">=dummy:" + fromCSN + ")" +
          "(" + HISTORICAL_ATTRIBUTE_NAME + "<=dummy:" + maxValueForId + "))");
    return conn.processSearch(
    return getRootConnection().processSearch(
      ByteString.valueOf(baseDN.toString()),
      SearchScope.WHOLE_SUBTREE,
      DereferenceAliasesPolicy.NEVER,
@@ -4364,7 +4309,7 @@
   * @throws Exception
   *           when raised.
   */
  public static InternalSearchOperation searchForChangedEntries(DN baseDN,
  static InternalSearchOperation searchForChangedEntries(DN baseDN,
      CSN fromCSN, InternalSearchListener resultListener) throws Exception
  {
    return searchForChangedEntries(baseDN, fromCSN, null, resultListener);
@@ -4465,17 +4410,17 @@
  {
    List<Attribute> attributes = new ArrayList<Attribute>();
    // get number of changes in the pending list
    addMonitorData(attributes, "pending-updates", getPendingUpdatesCount());
    // number of updates in the pending list
    addMonitorData(attributes, "pending-updates", pendingChanges.size());
    addMonitorData(attributes, "replayed-updates-ok",
        getNumReplayedPostOpCalled());
        numReplayedPostOpCalled.get());
    addMonitorData(attributes, "resolved-modify-conflicts",
        getNumResolvedModifyConflicts());
        numResolvedModifyConflicts.get());
    addMonitorData(attributes, "resolved-naming-conflicts",
        getNumResolvedNamingConflicts());
        numResolvedNamingConflicts.get());
    addMonitorData(attributes, "unresolved-naming-conflicts",
        getNumUnresolvedNamingConflicts());
        numUnresolvedNamingConflicts.get());
    addMonitorData(attributes, "remote-pending-changes-size",
        remotePendingChanges.getQueueSize());
@@ -4958,12 +4903,16 @@
     * Get an integer representation of the domain fractional configuration.
     * @return An integer representation of the domain fractional configuration.
     */
    int fractionalConfigToInt()
    private int fractionalConfigToInt()
    {
      if (!fractional)
      {
        return NOT_FRACTIONAL;
      if (fractionalExclusive)
      }
      else if (fractionalExclusive)
      {
        return EXCLUSIVE_FRACTIONAL;
      }
      return INCLUSIVE_FRACTIONAL;
    }
@@ -4976,23 +4925,29 @@
     * @throws ConfigException If some classes or attributes could not be
     * retrieved from the schema.
     */
    static boolean isFractionalConfigEquivalent(FractionalConfig cfg1,
    private static boolean isFractionalConfigEquivalent(FractionalConfig cfg1,
        FractionalConfig cfg2) throws ConfigException
    {
      // Compare base DNs just to be consistent
      if (!cfg1.getBaseDn().equals(cfg2.getBaseDn()))
      {
        return false;
      }
      // Compare modes
      if (cfg1.isFractional() != cfg2.isFractional()
          || cfg1.isFractionalExclusive() != cfg2.isFractionalExclusive())
      {
        return false;
      }
      // Compare all classes attributes
      Set<String> allClassesAttrs1 = cfg1.getFractionalAllClassesAttributes();
      Set<String> allClassesAttrs2 = cfg2.getFractionalAllClassesAttributes();
      if (!areAttributesEquivalent(allClassesAttrs1, allClassesAttrs2))
      {
        return false;
      }
      // Compare specific classes attributes
      Map<String, Set<String>> specificClassesAttrs1 =
@@ -5000,7 +4955,9 @@
      Map<String, Set<String>> specificClassesAttrs2 =
          cfg2.getFractionalSpecificClassesAttributes();
      if (specificClassesAttrs1.size() != specificClassesAttrs2.size())
      {
        return false;
      }
      /*
       * Check consistency of specific classes attributes
@@ -5036,13 +4993,17 @@
            Set<String> attributes1 = specificClassesAttrs1.get(className1);
            Set<String> attributes2 = specificClassesAttrs2.get(className2);
            if (!areAttributesEquivalent(attributes1, attributes2))
            {
              return false;
            }
            break;
          }
        }
        // Found matching class ?
        if (!foundClass)
        {
          return false;
        }
      }
      return true;
@@ -5120,9 +5081,10 @@
         USER_AND_REPL_OPERATIONAL_ATTRS, null);
     int count = 0;
     if (task != null)
     {
       task.setProgressStats(lastCSNPurgedFromHist, count);
     }
     for (SearchResultEntry entry : searchOp.getSearchEntries())
     {
@@ -5138,26 +5100,18 @@
       entryHist.setPurgeDelay(getHistoricalPurgeDelay());
       Attribute attr = entryHist.encodeAndPurge();
       count += entryHist.getLastPurgedValuesCount();
       List<Modification> mods = new LinkedList<Modification>();
       mods.add(new Modification(ModificationType.REPLACE, attr));
       List<Modification> mods =
           newList(new Modification(ModificationType.REPLACE, attr));
       ModifyOperation newOp = new ModifyOperationBasis(
             conn, InternalClientConnection.nextOperationID(),
             InternalClientConnection.nextMessageID(),
             new ArrayList<Control>(0),
             entry.getName(),
             mods);
      runAsSynchronizedOperation(newOp);
           conn, nextOperationID(), nextMessageID(), new ArrayList<Control>(0),
           entry.getName(), mods);
       runAsSynchronizedOperation(newOp);
       if (newOp.getResultCode() != ResultCode.SUCCESS)
       {
         // Log information for the repair tool.
         LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
         mb.append(ERR_CANNOT_ADD_CONFLICT_ATTRIBUTE.get());
         mb.append(newOp);
         mb.append(" ");
         mb.append(newOp.getResultCode());
         logger.error(mb.toMessage());
         logger.error(ERR_CANNOT_ADD_CONFLICT_ATTRIBUTE, newOp, newOp.getResultCode());
       }
       else if (task != null)
       {