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

gbellato
20.04.2007 c08575b5d155b34529c402d7e5398e77abc49117
These changes rovides the ability to repair
the consistency in the replication topology in the (hopefully) rare case
when hardware failure or software bugs could break it (issue 788 and 791)

There are several parts in this commit :
- a new log file called replication was added.
It's purpose is to store all the consistency errors detected by the replication
with enough information to allow the administrator to repair the problem.
This file is configured by default and contain only the replication errors,
To achieve the a new log severity keyword "None" has been created so that
the associated error log publisher does not print any error not related to
replication. (I will update the reference guide after this commit)

- a new control (the replication repair control) has been added.
When this control is used in a MODIFY, DELETE, ADD or MODDN operation
the operation is marked as a non-replicated replication operation.
This cause the following :
- The operation is allowed to modify attributes that are normally not
allowed to be modified or added (NO-USER-MODIFCATION) such as
entryuuid and ds-sync-hist
To achieve this I add to move those checks from the
AddOperationBasis.getObjectClasses() or AddOperationBasis.getUserAttributes()
to the LocalBackendWorkflowElement.processAdd() but this has already
been reviewed.
- no change number is associated to the operation.
- the operation is not published to the replication server and is therefore
a local only operation.
- the replication don't try to solve conflict or generate historical information
for this operation.

The intended usage of this control is that the administrator will check
for errors in the replication log, determine the entries that have inconsistent
values and use the control to repair them.
I will write some documentation explaining in more details how to do this.

- even though this is not related to replication repair I also took advantage of
this change to add the multimaster replication synchronization provider
in the default configuration.
This will make configuration of replication using dsconfig easier because
the user will now only need to configure the replication servers and
replication domains.
2 files added
14 files modified
547 ■■■■ changed files
opends/resource/config/config.ldif 29 ●●●●● patch | view | raw | blame | history
opends/resource/config/replication.ldif 14 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/ErrorLogPublisherConfiguration.xml 9 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/replication.properties 30 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/loggers/TextErrorLogPublisher.java 8 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/plugin/FakeOperation.java 4 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/plugin/Historical.java 17 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java 45 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java 5 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/plugin/ReplicationBroker.java 5 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/plugin/ReplicationDomain.java 43 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/plugin/ReplicationRepairRequestControl.java 130 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/ServerConstants.java 7 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java 41 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java 27 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationRepairControlTest.java 133 ●●●●● patch | view | raw | blame | history
opends/resource/config/config.ldif
@@ -601,6 +601,23 @@
ds-cfg-rotation-policy-dn: cn=Size Limit Rotation Policy,cn=Log Rotation Policies,cn=config
ds-cfg-retention-policy-dn: cn=File Count Retention Policy,cn=Log Retention Policies,cn=config
dn: cn=Replication Repair Logger,cn=Loggers,cn=config
objectClass: top
objectClass: ds-cfg-logger
objectClass: ds-cfg-error-logger
objectClass: ds-cfg-file-based-error-logger
cn: File-Based Error Logger
ds-cfg-logger-class: org.opends.server.loggers.TextErrorLogPublisher
ds-cfg-logger-enabled: true
ds-cfg-log-file: logs/replication
ds-cfg-log-file-mode: 640
ds-cfg-default-severity: none
ds-cfg-override-severity: SYNC=INFO,MILD_ERROR,MILD_WARNING,NOTICE
ds-cfg-asynchronous-writes: false
ds-cfg-rotation-policy-dn: cn=7 Days Time Limit Rotation Policy,cn=Log Rotation Policies,cn=config
ds-cfg-rotation-policy-dn: cn=Size Limit Rotation Policy,cn=Log Rotation Policies,cn=config
ds-cfg-retention-policy-dn: cn=File Count Retention Policy,cn=Log Retention Policies,cn=config
dn: cn=File-Based Debug Logger,cn=Loggers,cn=config
objectClass: top
objectClass: ds-cfg-logger
@@ -1921,3 +1938,15 @@
ds-cfg-num-worker-threads: 24
ds-cfg-max-work-queue-capacity: 0
dn: cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config
objectClass: top
objectClass: ds-cfg-synchronization-provider
objectClass: ds-cfg-multimaster-synchronization-provider
cn: Multimaster Synchronization
ds-cfg-synchronization-provider-enabled: true
ds-cfg-synchronization-provider-class: org.opends.server.replication.plugin.MultimasterReplication
dn: cn=domains,cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config
objectClass: top
objectClass: ds-cfg-branch
cn: domains
opends/resource/config/replication.ldif
@@ -9,20 +9,6 @@
# cn=Replication Server, cn=Multimaster Synchronization, cn=Synchronization Providers, cn=config
# entry.
dn: cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config
objectClass: top
objectClass: ds-cfg-synchronization-provider
objectClass: ds-cfg-multimaster-synchronization-provider
cn: Multimaster Synchronization
ds-cfg-synchronization-provider-enabled: true
ds-cfg-synchronization-provider-class: org.opends.server.replication.plugin.MultimasterReplication
dn: cn=domains,cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config
objectClass: top
objectClass: ds-cfg-branch
cn: domains
dn: cn=example,cn=domains,cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config
objectClass: top
objectClass: ds-cfg-replication-domain-config
opends/src/admin/defn/org/opends/server/admin/std/ErrorLogPublisherConfiguration.xml
@@ -57,6 +57,15 @@
            Messages of all severity levels will be logged.
          </adm:synopsis>
        </adm:value>
        <adm:value name="none">
          <adm:synopsis>
            No messages of any severity will be logged by default.
            This value is intended to be used in conjunction with
            the override-severity property to define an error
            logger that will publish no error message beside the errors
            of a given category.
          </adm:synopsis>
        </adm:value>
        <adm:value name="fatal-error">
          <adm:synopsis>
            The error log severity that will be used for messages that
opends/src/messages/messages/replication.properties
@@ -21,7 +21,12 @@
# CDDL HEADER END
#
#      Portions Copyright 2006-2007 Sun Microsystems, Inc.
#
#
# This file contains the primary Directory Server configuration.  It must not
# be directly edited while the server is online.  The server configuration
# should only be managed using the administration utilities provided with the
# Directory Server.
#
@@ -55,8 +60,6 @@
MILD_ERR_COULD_NOT_BIND_CHANGELOG_6=Changelog failed to start : could not \
 bind to the changelog listen port : %d. Error : %s
MILD_ERR_UNKNOWN_TYPE_7=Unknown operation type : %s
MILD_ERR_ERROR_REPLAYING_OPERATION_8=Error %s when replaying operation with \
 changenumber %s %s : %s
MILD_ERR_OPERATION_NOT_FOUND_IN_PENDING_9=Internal Error : Operation %s \
 change number %s was not found in pending list
MILD_ERR_COULD_NOT_INITIALIZE_DB_10=Changelog failed to start because the \
@@ -66,7 +69,7 @@
MILD_ERR_EXCEPTION_REPLAYING_OPERATION_12=An Exception was caught while \
 replaying operation %s : %s
MILD_ERR_NEED_CHANGELOG_PORT_13=The replication server port must be defined
MILD_ERR_ERROR_UPDATING_RUV_14=Error %s when updating server state %s : %s \
DEBUG_ERROR_UPDATING_RUV_14=Error %s when updating server state %s : %s \
 base dn : %s
MILD_ERR_ERROR_SEARCHING_RUV_15=Error %s when searching for server state %s : \
 %s base dn : %s
@@ -79,8 +82,8 @@
 server should be configured
NOTICE_EXCEPTION_STARTING_SESSION_20=Caught Exception during initial \
 communication with replication server :
NOTICE_CANNOT_RECOVER_CHANGES_21=Error when searching old changes from the \
 database
MILD_ERR_CANNOT_RECOVER_CHANGES_21=Error when searching old changes from the \
 database for base DN %s
NOTICE_COULD_NOT_FIND_CHANGELOG_WITH_MY_CHANGES_22=Could not find a \
 replication server that has seen all the local changes. Going to replay \
 changes
@@ -110,7 +113,7 @@
SEVERE_ERR_EXCEPTION_RECEIVING_REPLICATION_MESSAGE_34=An Exception was caught \
 while receiving replication message : %s
MILD_ERR_LOOP_REPLAYING_OPERATION_35=A loop was detected while replaying \
 operation: %s
 operation: %s error %s
MILD_ERR_FILE_CHECK_CREATE_FAILED_36=An Exception was caught while testing \
 existence or trying  to create the directory for the changelog database : %s
INFO_CHANGELOG_SERVER_ATTR_37=Specifies the list of replication servers to \
@@ -156,12 +159,12 @@
 have the same ServerId : %d
SEVERE_ERR_BAD_HISTORICAL_56=Entry %s was containing some unknown historical \
 information, This may cause some inconsistency for this entry
SEVERE_ERR_CANNOT_ADD_CONFLICT_ATTRIBUTE_57=A conflict was detected but the \
MILD_ERR_CANNOT_ADD_CONFLICT_ATTRIBUTE_57=A conflict was detected but the \
 conflict information could not be added. Operation :
SEVERE_ERR_CANNOT_RENAME_CONFLICT_ENTRY_58=An error happened trying the \
MILD_ERR_CANNOT_RENAME_CONFLICT_ENTRY_58=An error happened trying to \
 rename a conflicting entry :
SEVERE_ERR_EXCEPTION_RENAME_CONFLICT_ENTRY_59=An Exception happened when \
 trying the rename a conflicting entry :
MILD_ERR_EXCEPTION_RENAME_CONFLICT_ENTRY_59=An Exception happened when \
 trying to rename a conflicting entry :
SEVERE_ERR_CHANGELOG_UNSUPPORTED_UTF8_ENCODING_60=The JVM does not support \
 UTF-8. This is required to be able to encode the changes in the database. \
 This replication server will now shutdown
@@ -179,3 +182,8 @@
 and reopened
SEVERE_ERR_CHANGELOG_ERROR_SENDING_MSG_66=An unexpected error occurred  while \
 sending a Message to %s. This connection is going to be closed and reopened
MILD_ERR_ERROR_REPLAYING_OPERATION_67=Could not replay operation %s with \
 ChangeNumber %s error %s %s
MILD_ERR_UNKNOWN_ATTRIBUTE_IN_HISTORICAL_68=The entry %s has historical \
 information for attribute %s which is not defined in the schema. This \
 information will be ignored
opends/src/server/org/opends/server/loggers/TextErrorLogPublisher.java
@@ -194,6 +194,10 @@
          defaultSeverities.add(Severity.SEVERE_ERROR);
          defaultSeverities.add(Severity.SEVERE_WARNING);
        }
        else if (defSev.toString().equalsIgnoreCase(LOG_SEVERITY_NONE))
        {
          // don't add any severity
        }
        else
        {
          Severity errorSeverity =
@@ -424,6 +428,10 @@
          defaultSeverities.add(Severity.SEVERE_ERROR);
          defaultSeverities.add(Severity.SEVERE_WARNING);
        }
        else if (defSev.toString().equalsIgnoreCase(LOG_SEVERITY_NONE))
        {
          // don't add any severity
        }
        else
        {
          Severity errorSeverity =
opends/src/server/org/opends/server/replication/plugin/FakeOperation.java
@@ -80,9 +80,7 @@
   *
   * @param mod A modification that must be adde to the list of modifications
   *            included in this fake operation.
   * @throws Exception when the addition of this type of modification
   *         is not valid for this FakeOperation.
   */
  abstract public void addModification(Modification mod) throws Exception;
  abstract public void addModification(Modification mod);
}
opends/src/server/org/opends/server/replication/plugin/Historical.java
@@ -374,8 +374,11 @@
             * This attribute is unknown from the schema
             * Just skip it, the modification will be processed but no
             * historical information is going to be kept.
             * TODO : REPAIR tool should deal with this, add some logging.
             * Log information for the repair tool.
             */
            Message message = ERR_UNKNOWN_ATTRIBUTE_IN_HISTORICAL.get(
                entry.getDN().toNormalizedString(), histVal.getAttrString());
            logError(message);
            continue;
          }
@@ -453,19 +456,7 @@
          if (fakeOperation != null)
          {
            try
            {
              fakeOperation.addModification(mod);
            } catch (Exception e)
            {
              /*
               *  TODO : REPAIR : This Exception shows that there are some
               *  inconsistency in the historical information.
               *  This method can't fix the problem.
               *  This should be logged and somehow the repair
               *  service should get called to fix the problem.
               */
            }
          }
          else
          {
opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java
@@ -45,6 +45,7 @@
import org.opends.server.types.BackupConfig;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.Control;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
@@ -67,6 +68,8 @@
import org.opends.server.types.operation.PreOperationModifyOperation;
import org.opends.messages.Message;
import static org.opends.server.replication.plugin.
              ReplicationRepairRequestControl.*;
/**
 * This class is used to load the Replication code inside the JVM
@@ -92,20 +95,51 @@
   * Finds the domain for a given DN.
   *
   * @param dn   The DN for which the domain must be returned.
   * @param op   An optional operation for which the check is done.
   * @param pluginOp   An optional operation for which the check is done.
   *             Can be null is the request has no associated operation.
   * @return     The domain for this DN.
   */
  public static ReplicationDomain findDomain(DN dn, PluginOperation op)
  public static ReplicationDomain findDomain(DN dn, PluginOperation pluginOp)
  {
    /*
     * Don't run the special replication code on Operation that are
     * specifically marked as don't synchronize.
     */
    if ((op != null) && (op instanceof Operation) &&
        (((Operation) op).dontSynchronize()))
    if ((pluginOp != null) && (pluginOp instanceof Operation))
    {
        Operation op = ((Operation) pluginOp);
        if (op.dontSynchronize())
      return null;
        /*
         * Check if the provided operation is a repair operation and set
         * the synchronization flags if necessary.
         * The repair operations are tagged as synchronization operations
         * so that the core server let the operation modify the entryuuid
         * and ds-sync-hist attributes.
         * They are also tagged as dontSynchronize so that the replication
         * code running later do not generate ChnageNumber, solve conflicts
         * and forward the operation to the replication server.
         */
        for (Control c : op.getRequestControls())
        {
          if (c.getOID().equals(OID_REPLICATION_REPAIR_CONTROL))
          {
            op.setSynchronizationOperation(true);
            op.setDontSynchronize(true);
            // remove this control from the list of controls since
            // it has now been processed and the local backend will
            // fail if it finds a control that it does not know about and
            // that is marked as critical.
            List<Control> controls = op.getRequestControls();
            controls.remove(c);
            return null;
          }
        }
    }
    ReplicationDomain domain = null;
    DN temp = dn;
    do
@@ -188,6 +222,9 @@
    DirectoryServer.registerRestoreTaskListener(this);
    DirectoryServer.registerExportTaskListener(this);
    DirectoryServer.registerImportTaskListener(this);
    DirectoryServer.registerSupportedControl(
        ReplicationRepairRequestControl.OID_REPLICATION_REPAIR_CONTROL);
  }
  /**
opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
@@ -224,9 +224,10 @@
    op.run();
    ResultCode result = op.getResultCode();
    if (result != ResultCode.SUCCESS)
    if ((result != ResultCode.SUCCESS) &&
        ((result != ResultCode.NO_SUCH_OBJECT)))
    {
      Message message = ERR_ERROR_UPDATING_RUV.get(
      Message message = DEBUG_ERROR_UPDATING_RUV.get(
              op.getResultCode().getResultCodeName().toString(),
              op.toString(),
              op.getErrorMessage().toString(),
opends/src/server/org/opends/server/replication/plugin/ReplicationBroker.java
@@ -330,10 +330,11 @@
                   * An error happened trying to search for the updates
                   * This server will start acepting again new updates but
                   * some inconsistencies will stay between servers.
                   * TODO : REPAIR : log an error for the repair tool
                   * Log an error for the repair tool
                   * that will need to resynchronize the servers.
                   */
                  Message message = NOTE_CANNOT_RECOVER_CHANGES.get();
                  Message message = ERR_CANNOT_RECOVER_CHANGES.get(
                      baseDn.toNormalizedString());
                  logError(message);
                }
                else
opends/src/server/org/opends/server/replication/plugin/ReplicationDomain.java
@@ -1272,8 +1272,9 @@
      {
        // Continue with the next change but the servers could now become
        // inconsistent.
        // TODO : REPAIR : Should let the repair tool know about this
        Message message = ERR_LOOP_REPLAYING_OPERATION.get(op.toString());
        // Let the repair tool know about this.
        Message message = ERR_LOOP_REPLAYING_OPERATION.get(op.toString(),
            op.getErrorMessage().toString());
        logError(message);
        numUnresolvedNamingConflicts.incrementAndGet();
@@ -1306,7 +1307,7 @@
         * An Exception happened during the replay process.
         * Continue with the next change but the servers will now start
         * to be inconsistent.
         * TODO : REPAIR : Should let the repair tool know about this
         * Let the repair tool know about this.
         */
        Message message = ERR_EXCEPTION_REPLAYING_OPERATION.get(
            stackTraceToSingleLineString(e), op.toString());
@@ -1473,7 +1474,11 @@
    else
    {
      // The other type of errors can not be caused by naming conflicts.
      // TODO log a message for the repair tool.
      // Log a message for the repair tool.
      Message message = ERR_ERROR_REPLAYING_OPERATION.get(
          op.toString(), ctx.getChangeNumber().toString(),
          result.toString(), op.getErrorMessage().toString());
      logError(message);
      return true;
    }
  }
@@ -1538,7 +1543,11 @@
   else
   {
     // The other type of errors can not be caused by naming conflicts.
     // TODO log a message for the repair tool.
     // Log a message for the repair tool.
     Message message = ERR_ERROR_REPLAYING_OPERATION.get(
         op.toString(), ctx.getChangeNumber().toString(),
         result.toString(), op.getErrorMessage().toString());
     logError(message);
     return true;
   }
 }
@@ -1651,7 +1660,11 @@
  else
  {
    // The other type of errors can not be caused by naming conflicts.
    // TODO log a message for the repair tool.
    // Log a message for the repair tool.
    Message message = ERR_ERROR_REPLAYING_OPERATION.get(
        op.toString(), ctx.getChangeNumber().toString(),
        result.toString(), op.getErrorMessage().toString());
    logError(message);
    return true;
  }
}
@@ -1745,7 +1758,11 @@
    else
    {
      // The other type of errors can not be caused by naming conflicts.
      // TODO log a message for the repair tool.
      // log a message for the repair tool.
      Message message = ERR_ERROR_REPLAYING_OPERATION.get(
          op.toString(), ctx.getChangeNumber().toString(),
          result.toString(), op.getErrorMessage().toString());
      logError(message);
      return true;
    }
  }
@@ -1793,6 +1810,7 @@
      }
      else
      {
        // log error and information for the REPAIR tool.
        MessageBuilder mb = new MessageBuilder();
        mb.append(ERR_CANNOT_RENAME_CONFLICT_ENTRY.get());
        mb.append(String.valueOf(entryDN));
@@ -1801,10 +1819,10 @@
        mb.append(" ");
        mb.append(String.valueOf(op.getResultCode()));
        logError(mb.toMessage());
        // TODO : log error and information for the REPAIR tool.
      }
    } catch (DirectoryException e)
    {
      // log errror and information for the REPAIR tool.
      MessageBuilder mb = new MessageBuilder();
      mb.append(ERR_EXCEPTION_RENAME_CONFLICT_ENTRY.get());
      mb.append(String.valueOf(entryDN));
@@ -1813,7 +1831,6 @@
      mb.append(" ");
      mb.append(e.getLocalizedMessage());
      logError(mb.toMessage());
      // TODO log errror and information for the REPAIR tool.
    }
  }
@@ -1836,6 +1853,7 @@
    if (newOp.getResultCode() != ResultCode.SUCCESS)
    {
      // log information for the repair tool.
      MessageBuilder mb = new MessageBuilder();
      mb.append(ERR_CANNOT_RENAME_CONFLICT_ENTRY.get());
      mb.append(String.valueOf(dn));
@@ -1844,9 +1862,6 @@
      mb.append(" ");
      mb.append(String.valueOf(newOp.getResultCode()));
      logError(mb.toMessage());
      /*
       * TODO : REPAIR should log information for the repair tool.
       */
    }
  }
@@ -1876,15 +1891,13 @@
    ModifyOperation newOp = conn.processModify(currentDN, mods);
    if (newOp.getResultCode() != ResultCode.SUCCESS)
    {
      // Log information for the repair tool.
      MessageBuilder mb = new MessageBuilder();
      mb.append(ERR_CANNOT_ADD_CONFLICT_ATTRIBUTE.get());
      mb.append(String.valueOf(op));
      mb.append(" ");
      mb.append(String.valueOf(newOp.getResultCode()));
      logError(mb.toMessage());
      /*
       * TODO : REPAIR should log information for the repair tool.
       */
    }
  }
opends/src/server/org/opends/server/replication/plugin/ReplicationRepairRequestControl.java
New file
@@ -0,0 +1,130 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.replication.plugin;
import org.opends.server.types.Control;
import org.opends.server.types.LDAPException;
/**
 * This class implements the Sun-defined replication repair control.
 * This control can be used to modify the content of a replicated database
 * on a single server without impacting the other servers that are replicated
 * with this server.
 * It also allows to modify attributes like entryuuid and ds-sync-hist that
 * are normally not modifiable from an external connection.
 */
public class ReplicationRepairRequestControl
       extends Control
{
  /**
   * The OID of the Replication repair Control.
   */
  public static final String
          OID_REPLICATION_REPAIR_CONTROL = "1.3.6.1.4.1.26027.1.5.2";
  /**
   * Creates a new instance of the replication repair request control with the
   * default settings.
   */
  public ReplicationRepairRequestControl()
  {
    super(OID_REPLICATION_REPAIR_CONTROL, false);
  }
  /**
   * Creates a new instance of the replication repair control with the
   * provided information.
   *
   * @param  oid         The OID to use for this control.
   * @param  isCritical  Indicates whether support for this control should be
   *                     considered a critical part of the client processing.
   */
  public ReplicationRepairRequestControl(String oid, boolean isCritical)
  {
    super(oid, isCritical);
  }
  /**
   * Creates a new replication repair request control from the contents of the
   * provided control.
   *
   * @param  control  The generic control containing the information to use to
   *                  create this replication repair request control.
   *
   * @return  The replication repair request control decoded from the provided
   *          control.
   *
   * @throws  LDAPException  If this control cannot be decoded as a valid
   *                         replication repair request control.
   */
  public static ReplicationRepairRequestControl decodeControl(Control control)
         throws LDAPException
  {
    return new ReplicationRepairRequestControl(control.getOID(),
                                           control.isCritical());
  }
  /**
   * Retrieves a string representation of this replication repair request
   * control.
   *
   * @return  A string representation of this replication repair request
   *          control.
   */
  public String toString()
  {
    StringBuilder buffer = new StringBuilder();
    toString(buffer);
    return buffer.toString();
  }
  /**
   * Appends a string representation of this replication repair request control
   * to the provided buffer.
   *
   * @param  buffer  The buffer to which the information should be appended.
   */
  public void toString(StringBuilder buffer)
  {
    buffer.append("ReplicationRepairRequestControl()");
  }
}
opends/src/server/org/opends/server/util/ServerConstants.java
@@ -903,6 +903,13 @@
   */
  public static final String LOG_SEVERITY_ALL = "all";
  /**
   * The English name for the basic none log severity used to log
   * no error message beside some specific category.
   */
  public static final String LOG_SEVERITY_NONE = "none";
  /**
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -4702,6 +4702,47 @@
          }
        }
        // If the attribute type is marked "NO-USER-MODIFICATION" then fail
        // unless this is an internal operation or is related to
        // synchronization in some way.
        // This must be done before running the password policy code
        // and any other code that may add attributes marked as
        // "NO-USER-MODIFICATION"
        //
        // Note that doing this checks at this time
        // of the processing does not make it possible for pre-parse plugins
        // to add NO-USER-MODIFICATION attributes to the entry.
        for (AttributeType at : userAttributes.keySet())
        {
          if (at.isNoUserModification())
          {
            if (! (localOp.isInternalOperation() ||
              localOp.isSynchronizationOperation()))
            {
              localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
              localOp.appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get(
                  String.valueOf(entryDN), at.getNameOrOID()));
              break addProcessing;
            }
          }
        }
        for (AttributeType at : operationalAttributes.keySet())
        {
          if (at.isNoUserModification())
          {
            if (! (localOp.isInternalOperation() ||
              localOp.isSynchronizationOperation()))
            {
              localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
              localOp.appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get(
                  String.valueOf(entryDN), at.getNameOrOID()));
              break addProcessing;
            }
          }
        }
        // Check to see if the entry already exists.  We do this before
        // checking whether the parent exists to ensure a referral entry
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
@@ -322,33 +322,6 @@
   */
  protected void configureReplication() throws Exception
  {
    // Add the Multimaster replication plugin
    String synchroPluginLdif = "dn: " + synchroPluginStringDN + "\n"
         + "objectClass: top\n"
         + "objectClass: ds-cfg-synchronization-provider\n"
         + "objectClass: ds-cfg-multimaster-synchronization-provider\n"
         + "ds-cfg-synchronization-provider-enabled: true\n"
         + "ds-cfg-synchronization-provider-class: " +
         "org.opends.server.replication.plugin.MultimasterReplication\n";
    Entry synchroPluginEntry = TestCaseUtils.entryFromLdifString(synchroPluginLdif);
    DirectoryServer.getConfigHandler().addEntry(synchroPluginEntry, null);
    configEntryList.add(synchroPluginEntry.getDN());
    assertNotNull(DirectoryServer.getConfigEntry(DN
        .decode(synchroPluginStringDN)),
        "Unable to add the Multimaster replication plugin");
    // domains container entry.
    String domainsLdif = "dn: "
      + "cn=domains," + synchroPluginStringDN + "\n"
      + "objectClass: top\n"
      + "objectClass: ds-cfg-branch\n";
    Entry domainsEntry = TestCaseUtils.entryFromLdifString(domainsLdif);
    DirectoryServer.getConfigHandler().addEntry(domainsEntry, null);
    configEntryList.add(domainsEntry.getDN());
    assertNotNull(DirectoryServer.getConfigEntry(
      DN.decode(synchroPluginStringDN)),
      "Unable to add the Multimaster replication plugin");
    if (replServerEntry != null)
    {
      // Add the replication server
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationRepairControlTest.java
New file
@@ -0,0 +1,133 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.replication.plugin;
import org.opends.server.TestCaseUtils;
import org.opends.server.replication.ReplicationTestCase;
import org.opends.server.tools.LDAPModify;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
public class ReplicationRepairControlTest extends ReplicationTestCase
{
  @Test()
  public void testRepairControl()
         throws Exception
  {
    TestCaseUtils.startServer();
    TestCaseUtils.initializeTestBackend(true);
    // Test that we can't add an entry with the entryuuid attribute
    // without specifying the replication repair control.
    String path = TestCaseUtils.createTempFile(
        "dn: uid=test.repair,o=test\n" +
        "changetype: add\n" +
        "objectClass: top\n" +
        "objectClass: person" +
        "objectClass: organizationalPerson" +
        "objectClass: inetOrgPerson" +
        "uid: test.repair" +
        "givenName: Test" +
        "sn: User" +
        "cn: Test User" +
        "userPassword: password" +
        "entryuuid: d5b910d8-47cb-4ac0-9e5f-0f4a77de58d4");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-a",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, null), 65);
    // Test that we can't add an entry with the ds-sync-hist attribute
    // without specifying the replication repair control.
    String path1 = TestCaseUtils.createTempFile(
        "dn: uid=test.repair,o=test\n" +
        "changetype: add\n" +
        "objectClass: top\n" +
        "objectClass: person" +
        "objectClass: organizationalPerson" +
        "objectClass: inetOrgPerson" +
        "uid: test.repair" +
        "givenName: Test" +
        "sn: User" +
        "cn: Test User" +
        "userPassword: password" +
        "ds-sync-hist: description:00000108b3a6cbb800000001:del:deleted_value");
    String[] args1 =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-a",
      "-f", path1
    };
    assertEquals(LDAPModify.mainModify(args1, false, null, null), 65);
    // Now Test specifying the replication repair control makes
    // possible to add an entry with the entryuuid and ds-sync-hist attributes
    // (notice the -J repairControlOid in the ldapmodify arguments)
    String path2 = TestCaseUtils.createTempFile(
        "dn: uid=test.repair,o=test\n" +
        "changetype: add\n" +
        "objectClass: top\n" +
        "objectClass: person" +
        "objectClass: organizationalPerson" +
        "objectClass: inetOrgPerson" +
        "uid: test.repair" +
        "givenName: Test" +
        "sn: User" +
        "cn: Test User" +
        "userPassword: password" +
        "ds-sync-hist: description:00000108b3a6cbb800000001:del:deleted_value" +
        "entryuuid: d5b910d8-47cb-4ac0-9e5f-0f4a77de58d4");
    String[] args2 =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-J", "1.3.6.1.4.1.26027.1.5.2",
      "-a",
      "-f", path2
    };
    assertEquals(LDAPModify.mainModify(args2, false, null, null), 0);
  }
}