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

Jean-Noel Rouvignac
11.17.2014 bdcbee41067e1f439a8ebe7fd0a9dd01799b593a
opendj3-server-dev/src/guitools/org/opends/guitools/controlpanel/browser/IconPool.java
@@ -22,20 +22,10 @@
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2013 ForgeRock AS.
 *      Portions Copyright 2013-2014 ForgeRock AS.
 */
package org.opends.guitools.controlpanel.browser;
import static org.opends.messages.AdminToolMessages.*;
import java.awt.Canvas;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.MemoryImageSource;
import java.awt.image.PixelGrabber;
import java.util.HashMap;
import java.util.Set;
import java.util.SortedSet;
@@ -46,6 +36,8 @@
import org.opends.quicksetup.ui.UIFactory;
import org.opends.server.util.ServerConstants;
import static org.opends.messages.AdminToolMessages.*;
/**
 * This class is used as a cache containing the icons that are used by the
 * BrowserController to update the nodes.  It keeps some icons associated with
@@ -66,10 +58,11 @@
   */
  public static final int MODIFIER_ERROR    = 0x04;
  private HashMap<String, ImageIcon> iconTable =
  private final HashMap<String, ImageIcon> iconTable =
    new HashMap<String, ImageIcon>();
  private HashMap<String, String> pathTable = new HashMap<String, String>();
  private HashMap<String, String> descriptionTable =
  private final HashMap<String, String> pathTable =
      new HashMap<String, String>();
  private final HashMap<String, String> descriptionTable =
    new HashMap<String, String>();
  private ImageIcon defaultLeafIcon;
  private ImageIcon suffixIcon;
@@ -117,12 +110,9 @@
    "passwordpolicy", INFO_PASSWORD_POLICY_ICON_DESCRIPTION.get().toString()
  };
  private String GENERIC_OBJECT_DESCRIPTION = "Generic entry";
  private final String GENERIC_OBJECT_DESCRIPTION = "Generic entry";
  /**
   * The default constructor.
   *
   */
  /** The default constructor. */
  public IconPool() {
    // Recopy ICON_PATH in pathTable for fast access
    for (int i = 0; i < ICON_PATH.length; i = i+2) {
@@ -144,15 +134,12 @@
   * modifiers.
   */
  public ImageIcon getIcon(SortedSet<String> objectClasses, int modifiers) {
    ImageIcon result;
    String key = makeKey(objectClasses, modifiers);
    result = iconTable.get(key);
    ImageIcon result = iconTable.get(key);
    if (result == null) {
      result = makeIcon(objectClasses, modifiers);
      iconTable.put(key, result);
    }
    return result;
  }
@@ -270,7 +257,7 @@
    ImageIcon result;
    // Find the icon associated to the object class
    if ((objectClasses == null) || (objectClasses.size() == 0)) {
    if (objectClasses == null || objectClasses.size() == 0) {
      result = getDefaultContainerIcon();
    }
    else {
@@ -328,87 +315,8 @@
    if(ocValues != null) {
      result.append(Utilities.getStringFromCollection(ocValues, ""));
    }
    result.append(String.valueOf(modifiers));
    result.append(modifiers);
    return result.toString();
  }
    /**
     * Returns a RemoteImage corresponding to the superposition of the icon
     * Image and the mask Image.
     *
     * @param icon the RemoteImage that we want to bar.
     * @param mask the ImageIcond to be used as mask.
     * @return a RemoteImage corresponding to the superposition of the icon
     * Image and the mask Image.
     */
  public static ImageIcon maskedIcon(ImageIcon icon, ImageIcon mask) {
    ImageIcon fReturn;
    int TRANSPARENT = 16711165;  // The value of a transparent pixel
    int h = icon.getIconHeight();
    int w = icon.getIconWidth();
    if (mask.getImageLoadStatus() != MediaTracker.COMPLETE) {
      return null;
    }
    Image maskImage = mask.getImage();
    Image scaledMaskImage = maskImage.getScaledInstance(w, h ,
        Image.SCALE_SMOOTH);
    ImageIcon scaledMask = new ImageIcon(scaledMaskImage);
    if (scaledMask.getImageLoadStatus() != MediaTracker.COMPLETE) {
      return null;
    }
    int[] iconPixels = new int[w * h];
    try {
      PixelGrabber pg =
        new PixelGrabber(icon.getImage(), 0, 0, w, h, iconPixels, 0, w);
      pg.grabPixels();
      if ((pg.status() & ImageObserver.ABORT) !=0) {
        return null;
      }
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
    int[] filterPixels = new int[w * h];
    try {
      PixelGrabber pgf =
        new PixelGrabber(scaledMask.getImage(), 0, 0, w, h, filterPixels, 0, w);
      pgf.grabPixels();
      if ((pgf.status() & ImageObserver.ABORT) !=0) {
        fReturn = null;
        return fReturn;
      }
    } catch (Exception e) {
      e.printStackTrace();
      fReturn = null;
      return fReturn;
    }
    int[] newPixels = new int[w * h];
    for( int i = 0; i < h; i++)
      for (int j = 0; j < w; j++)
        if (filterPixels[j + i*w] != TRANSPARENT) {
          newPixels[j + i*w] = filterPixels[j + i*w];
        } else {
          newPixels[j + i*w] = iconPixels[j + i*w];
        }
    Canvas component = new Canvas();
    Image newImage = component.getToolkit().createImage(
        new MemoryImageSource(
            w, h, ColorModel.getRGBdefault(), newPixels, 0, w));
    fReturn = new ImageIcon(newImage, icon.getDescription());
    return fReturn;
  }
}
opendj3-server-dev/src/guitools/org/opends/guitools/controlpanel/browser/LDAPConnectionPool.java
@@ -27,7 +27,6 @@
package org.opends.guitools.controlpanel.browser;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import javax.naming.NamingException;
@@ -44,6 +43,8 @@
import com.forgerock.opendj.cli.CliConstants;
import static org.opends.admin.ads.util.ConnectionUtils.*;
/**
 * An LDAPConnectionPool is a pool of LDAPConnection.
 * <BR><BR>
@@ -73,11 +74,12 @@
 */
public class LDAPConnectionPool {
  HashMap<String, AuthRecord> authTable = new HashMap<String, AuthRecord>();
  HashMap<String, ConnectionRecord> connectionTable =
  private final HashMap<String, AuthRecord> authTable =
      new HashMap<String, AuthRecord>();
  private final HashMap<String, ConnectionRecord> connectionTable =
    new HashMap<String, ConnectionRecord>();
  ArrayList<ReferralAuthenticationListener> listeners;
  private ArrayList<ReferralAuthenticationListener> listeners;
  private Control[] requestControls = new Control[] {};
  private ApplicationTrustManager trustManager;
@@ -91,28 +93,20 @@
   * connection pool, <CODE>false</CODE> otherwise.
   */
  public boolean isConnectionRegistered(InitialLdapContext ctx) {
    boolean isConnectionRegistered = false;
    for (String key : connectionTable.keySet())
    {
      ConnectionRecord cr = connectionTable.get(key);
      if (cr.ctx != null) {
        isConnectionRegistered =
         ConnectionUtils.getHostName(cr.ctx).equals(
             ConnectionUtils.getHostName(ctx)) &&
        (ConnectionUtils.getPort(cr.ctx) == ConnectionUtils.getPort(ctx)) &&
        ConnectionUtils.getBindDN(cr.ctx).equals(
            ConnectionUtils.getBindDN(ctx)) &&
        ConnectionUtils.getBindPassword(cr.ctx).equals(
            ConnectionUtils.getBindPassword(ctx)) &&
        (ConnectionUtils.isSSL(cr.ctx) == ConnectionUtils.isSSL(ctx)) &&
        (ConnectionUtils.isStartTLS(cr.ctx) == ConnectionUtils.isStartTLS(ctx));
      }
      if (isConnectionRegistered)
      {
        break;
      if (cr.ctx != null
          && getHostName(cr.ctx).equals(getHostName(ctx))
          && getPort(cr.ctx) == getPort(ctx)
          && getBindDN(cr.ctx).equals(getBindDN(ctx))
          && getBindPassword(cr.ctx).equals(getBindPassword(ctx))
          && isSSL(cr.ctx) == isSSL(ctx)
          && isStartTLS(cr.ctx) == isStartTLS(ctx)) {
        return true;
      }
    }
    return isConnectionRegistered;
    return false;
  }
  /**
@@ -121,12 +115,7 @@
   */
  public void registerConnection(InitialLdapContext ctx) {
    registerAuth(ctx);
    LDAPURL url = makeLDAPUrl(
                  ConnectionUtils.getHostName(ctx),
                  ConnectionUtils.getPort(ctx),
                  "",
                  ConnectionUtils.isSSL(ctx)
                  );
    LDAPURL url = makeLDAPUrl(ctx);
    String key = makeKeyFromLDAPUrl(url);
    ConnectionRecord cr = new ConnectionRecord();
    cr.ctx = ctx;
@@ -143,11 +132,7 @@
  public void unregisterConnection(InitialLdapContext ctx)
  throws NamingException
  {
    LDAPURL url = makeLDAPUrl(
        ConnectionUtils.getHostName(ctx),
        ConnectionUtils.getPort(ctx),
        "",
        ConnectionUtils.isSSL(ctx));
    LDAPURL url = makeLDAPUrl(ctx);
    unRegisterAuth(url);
    String key = makeKeyFromLDAPUrl(url);
    connectionTable.remove(key);
@@ -166,17 +151,6 @@
  }
  /**
   * Removes a referral authentication listener.
   * @param listener the referral authentication listener.
   */
  public void removeReferralAuthenticationListener(
      ReferralAuthenticationListener listener) {
    if (listeners != null) {
      listeners.remove(listener);
    }
  }
  /**
   * Returns an LDAPConnection for accessing the specified url.
   * If no connection are available for the protocol/host/port
   * of the URL, getConnection() makes a new one and call connect().
@@ -289,35 +263,17 @@
    if (targetRecord == null) { // ldc is not in _connectionTable -> bug
      throw new IllegalArgumentException("Invalid LDAP connection");
    }
    else {
      synchronized(targetRecord) {
        targetRecord.counter--;
        if ((targetRecord.counter == 0) && targetRecord.disconnectAfterUse) {
          disconnectAndRemove(targetRecord);
        }
      }
    }
  }
  /**
   * Disconnect the connections which are not being used.
   * Connections being used will be disconnected as soon
   * as they are released.
   */
  public synchronized void flush() {
    for (ConnectionRecord cr : connectionTable.values())
    synchronized (targetRecord)
    {
      if (cr.counter <= 0) {
        disconnectAndRemove(cr);
      }
      else {
        cr.disconnectAfterUse = true;
      targetRecord.counter--;
      if (targetRecord.counter == 0 && targetRecord.disconnectAfterUse)
      {
        disconnectAndRemove(targetRecord);
      }
    }
  }
  /**
   * Register authentication data.
   * If authentication data are already available for the protocol/host/port
@@ -333,14 +289,11 @@
   * provided authentication (for testing purposes).
   * @throws NamingException if an error occurs connecting.
   */
  public void registerAuth(LDAPURL ldapUrl, String dn, String pw,
      boolean connect)
  throws NamingException {
  private void registerAuth(LDAPURL ldapUrl, String dn, String pw,
      boolean connect) throws NamingException {
    String key = makeKeyFromLDAPUrl(ldapUrl);
    AuthRecord ar;
    ar = new AuthRecord();
    ar.ldapUrl  = ldapUrl;
    final AuthRecord ar = new AuthRecord();
    ar.dn       = dn;
    ar.password = pw;
@@ -373,15 +326,10 @@
   * @param ctx the connection that we retrieve the authentication information
   * from.
   */
  public void registerAuth(InitialLdapContext ctx) {
    LDAPURL url = makeLDAPUrl(
      ConnectionUtils.getHostName(ctx),
      ConnectionUtils.getPort(ctx),
      "",
      ConnectionUtils.isSSL(ctx));
  private void registerAuth(InitialLdapContext ctx) {
    LDAPURL url = makeLDAPUrl(ctx);
    try {
      registerAuth(url, ConnectionUtils.getBindDN(ctx),
          ConnectionUtils.getBindPassword(ctx), false);
      registerAuth(url, getBindDN(ctx), getBindPassword(ctx), false);
    }
    catch (NamingException x) {
      throw new RuntimeException("Bug");
@@ -397,7 +345,7 @@
   * unregistered.
   * @throws NamingException if the unbind fails.
   */
  public void unRegisterAuth(LDAPURL ldapUrl) throws NamingException {
  private void unRegisterAuth(LDAPURL ldapUrl) throws NamingException {
    String key = makeKeyFromLDAPUrl(ldapUrl);
    authTable.remove(key);
@@ -405,45 +353,6 @@
  }
  /**
   * Get authentication DN registered for this url.
   * @param ldapUrl the LDAP URL for which we want to get authentication DN.
   * @return the bind DN of the authentication.
   */
  public synchronized String getAuthDN(LDAPURL ldapUrl) {
    String result;
    String key = makeKeyFromLDAPUrl(ldapUrl);
    AuthRecord ar = authTable.get(key);
    if (ar == null) {
      result = null;
    }
    else {
      result = ar.dn;
    }
    return result;
  }
  /**
   * Get authentication password registered for this url.
   * @param ldapUrl the LDAP URL for which we want to get authentication
   * password.
   * @return the password of the authentication.
   */
  public synchronized String getAuthPassword(LDAPURL ldapUrl) {
    String result;
    String key = makeKeyFromLDAPUrl(ldapUrl);
    AuthRecord ar = authTable.get(key);
    if (ar == null) {
      result = null;
    }
    else {
      result = ar.password;
    }
    return result;
  }
  /**
   * Disconnect the connection associated to a record
   * and remove the record from connectionTable.
   * @param cr the ConnectionRecord to remove.
@@ -492,8 +401,7 @@
   */
  private static String makeKeyFromRecord(ConnectionRecord rec) {
    String protocol = ConnectionUtils.isSSL(rec.ctx) ? "LDAPS" : "LDAP";
    return protocol + ":" + ConnectionUtils.getHostName(rec.ctx) + ":" +
    ConnectionUtils.getPort(rec.ctx);
    return protocol + ":" + getHostName(rec.ctx) + ":" + getPort(rec.ctx);
  }
  /**
@@ -507,24 +415,18 @@
  private InitialLdapContext createLDAPConnection(LDAPURL ldapUrl,
      AuthRecord ar) throws NamingException
  {
    InitialLdapContext ctx;
    // Take the base DN out of the URL and only keep the protocol, host and port
    ldapUrl = new LDAPURL(ldapUrl.getScheme(), ldapUrl.getHost(),
          ldapUrl.getPort(), (DN)null, null, null, null, null);
    if (isSecureLDAPUrl(ldapUrl))
    {
      ctx = ConnectionUtils.createLdapsContext(ldapUrl.toString(), ar.dn,
      return ConnectionUtils.createLdapsContext(ldapUrl.toString(), ar.dn,
          ar.password, getConnectTimeout(), null,
          getTrustManager() , getKeyManager());
          getTrustManager(), getKeyManager());
    }
    else
    {
      ctx = ConnectionUtils.createLdapContext(ldapUrl.toString(), ar.dn,
          ar.password, getConnectTimeout(), null);
    }
    return ctx;
    return ConnectionUtils.createLdapContext(ldapUrl.toString(), ar.dn,
        ar.password, getConnectTimeout(), null);
  }
  /**
@@ -581,26 +483,16 @@
   * @return <CODE>true</CODE> if the LDAP URL is secure and <CODE>false</CODE>
   * otherwise.
   */
  public static boolean isSecureLDAPUrl(LDAPURL url) {
  private static boolean isSecureLDAPUrl(LDAPURL url) {
    return !LDAPURL.DEFAULT_SCHEME.equalsIgnoreCase(url.getScheme());
  }
  /**
   * Make an url from the specified arguments.
   * @param host the host.
   * @param port the port.
   * @param dn the dn.
   * @param secure whether it is a secure URL or not.
   * @return an LDAP URL from the specified arguments.
   */
  public static LDAPURL makeLDAPUrl(String host, int port, String dn,
      boolean secure) {
  private LDAPURL makeLDAPUrl(InitialLdapContext ctx) {
    return new LDAPURL(
        secure ? "ldaps" : LDAPURL.DEFAULT_SCHEME,
            host,
            port,
            dn,
        isSSL(ctx) ? "ldaps" : LDAPURL.DEFAULT_SCHEME,
            getHostName(ctx),
            getPort(ctx),
            "",
            null, // no attributes
            SearchScope.BASE_OBJECT,
            null, // No filter
@@ -645,20 +537,12 @@
        null); // No extensions
  }
  /**
   * Returns a collection of AuthRecord.
   * @return a collection of AuthRecord.
   */
  Collection<?> getRegisteredAuthentication() {
    return authTable.values();
  }
}
/**
 * A structure representing authentication data.
 */
class AuthRecord {
  LDAPURL ldapUrl;
  String dn;
  String password;
}
opendj3-server-dev/src/server/org/opends/server/replication/plugin/PersistentServerState.java
@@ -27,7 +27,7 @@
package org.opends.server.replication.plugin;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -57,7 +57,7 @@
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
   private final DN baseDn;
   private final DN baseDN;
   private final int serverId;
   private final ServerState state;
@@ -70,13 +70,13 @@
   * Create a new PersistentServerState based on an already existing
   * ServerState.
   *
   * @param baseDn    The baseDN for which the ServerState is created.
   * @param baseDN    The baseDN for which the ServerState is created.
   * @param serverId  The serverId.
   * @param state     The serverState.
   */
  PersistentServerState(DN baseDn, int serverId, ServerState state)
  PersistentServerState(DN baseDN, int serverId, ServerState state)
  {
    this.baseDn = baseDn;
    this.baseDN = baseDN;
    this.serverId = serverId;
    this.state = state;
    loadState();
@@ -114,7 +114,7 @@
  {
    if (!state.isSaved())
    {
      state.setSaved(updateStateEntry() == ResultCode.SUCCESS);
      state.setSaved(updateStateEntry());
    }
  }
@@ -164,16 +164,15 @@
       * Search the database entry that is used to periodically
       * save the ServerState
       */
      LinkedHashSet<String> attributes = new LinkedHashSet<String>(1);
      attributes.add(REPLICATION_STATE);
      final InternalSearchOperation search = getRootConnection().processSearch(
          baseDn, SearchScope.BASE_OBJECT, DereferenceAliasesPolicy.NEVER,
          0, 0, false, filter, attributes);
      if (((search.getResultCode() != ResultCode.SUCCESS)) &&
          ((search.getResultCode() != ResultCode.NO_SUCH_OBJECT)))
          baseDN, SearchScope.BASE_OBJECT, DereferenceAliasesPolicy.NEVER,
          0, 0, false, filter, Collections.singleton(REPLICATION_STATE));
      final ResultCode resultCode = search.getResultCode();
      if (resultCode != ResultCode.SUCCESS
          && resultCode != ResultCode.NO_SUCH_OBJECT)
      {
        logger.error(ERR_ERROR_SEARCHING_RUV, search.getResultCode().getName(), search,
                search.getErrorMessage(), baseDn);
                search.getErrorMessage(), baseDN);
        return null;
      }
      return getFirstResult(search);
@@ -197,15 +196,13 @@
    {
      SearchFilter filter = SearchFilter.createFilterFromString(
          "(&(objectclass=ds-cfg-replication-domain)"
          + "(ds-cfg-base-dn=" + baseDn + "))");
          + "(ds-cfg-base-dn=" + baseDN + "))");
      LinkedHashSet<String> attributes = new LinkedHashSet<String>(1);
      attributes.add(REPLICATION_STATE);
      final InternalSearchOperation op = getRootConnection().processSearch(
          DN.valueOf("cn=config"),
          SearchScope.SUBORDINATES,
          DereferenceAliasesPolicy.NEVER,
          1, 0, false, filter, attributes);
          1, 0, false, filter, Collections.singleton(REPLICATION_STATE));
      return getFirstResult(op);
    }
    catch (DirectoryException e)
@@ -238,12 +235,10 @@
  {
    AttributeType synchronizationStateType =
      DirectoryServer.getAttributeType(REPLICATION_STATE);
    List<Attribute> attrs =
      resultEntry.getAttribute(synchronizationStateType);
    List<Attribute> attrs = resultEntry.getAttribute(synchronizationStateType);
    if (attrs != null)
    {
      Attribute attr = attrs.get(0);
      for (ByteString value : attr)
      for (ByteString value : attrs.get(0))
      {
        update(new CSN(value.toString()));
      }
@@ -254,12 +249,12 @@
   * Save the current values of this PersistentState object
   * in the appropriate entry of the database.
   *
   * @return a ResultCode indicating if the method was successful.
   * @return a boolean indicating if the method was successful.
   */
  private ResultCode updateStateEntry()
  private boolean updateStateEntry()
  {
    // Generate a modify operation on the Server State baseDN Entry.
    ResultCode result = runUpdateStateEntry(baseDn);
    ResultCode result = runUpdateStateEntry(baseDN);
    if (result == ResultCode.NO_SUCH_OBJECT)
    {
      // The base entry does not exist yet in the database or has been deleted,
@@ -267,11 +262,10 @@
      SearchResultEntry configEntry = searchConfigEntry();
      if (configEntry != null)
      {
        DN configDN = configEntry.getName();
        result = runUpdateStateEntry(configDN);
        result = runUpdateStateEntry(configEntry.getName());
      }
    }
    return result;
    return result == ResultCode.SUCCESS;
  }
  /**
@@ -287,14 +281,12 @@
    ArrayList<ByteString> values = state.toASN1ArrayList();
    LDAPAttribute attr = new LDAPAttribute(REPLICATION_STATE, values);
    LDAPModification mod = new LDAPModification(ModificationType.REPLACE, attr);
    ArrayList<RawModification> mods = new ArrayList<RawModification>(1);
    mods.add(mod);
    RawModification mod = new LDAPModification(ModificationType.REPLACE, attr);
    ModifyOperationBasis op = new ModifyOperationBasis(getRootConnection(),
          nextOperationID(), nextMessageID(), null,
          ByteString.valueOf(serverStateEntryDN.toString()),
          mods);
          Collections.singletonList(mod));
    op.setInternalOperation(true);
    op.setSynchronizationOperation(true);
    op.setDontSynchronize(true);
@@ -302,7 +294,7 @@
    if (op.getResultCode() != ResultCode.SUCCESS)
    {
      logger.error(DEBUG_ERROR_UPDATING_RUV,
          op.getResultCode().getName(), op, op.getErrorMessage(), baseDn);
          op.getResultCode().getName(), op, op.getErrorMessage(), baseDN);
    }
    return op.getResultCode();
  }
@@ -357,7 +349,7 @@
      InternalSearchOperation op;
      try
      {
        op = LDAPReplicationDomain.searchForChangedEntries(baseDn,
        op = LDAPReplicationDomain.searchForChangedEntries(baseDN,
                serverStateMaxCSN, null);
      }
      catch (Exception  e)
@@ -369,7 +361,7 @@
      {
        // An error happened trying to search for the updates
        // Log an error
        logger.error(ERR_CANNOT_RECOVER_CHANGES, baseDn.toNormalizedString());
        logger.error(ERR_CANNOT_RECOVER_CHANGES, baseDN.toNormalizedString());
        return;
      }
@@ -394,7 +386,7 @@
      {
        // Update the serverState with the new maxCsn present in the database
        update(dbMaxCSN);
        logger.info(NOTE_SERVER_STATE_RECOVERY, baseDn.toNormalizedString(), dbMaxCSN);
        logger.info(NOTE_SERVER_STATE_RECOVERY, baseDN.toNormalizedString(), dbMaxCSN);
      }
    }
  }
@@ -410,4 +402,14 @@
  {
    return state.getCSN(serverId);
  }
  /** {@inheritDoc} */
  @Override
  public String toString()
  {
    return getClass().getSimpleName()
        + " baseDN=" + baseDN
        + " serverId=" + serverId
        + " " + REPLICATION_STATE + "=" + state;
  }
}
opendj3-server-dev/src/server/org/opends/server/replication/server/MessageHandler.java
@@ -292,31 +292,11 @@
           *           restart as usual
           *   load this change on the delayList
           */
          DBCursor<UpdateMsg> cursor = null;
          try
          {
            // fill the lateQueue
            cursor = replicationServerDomain.getCursorFrom(serverState);
            while (cursor.next() && isLateQueueBelowThreshold())
            {
              lateQueue.add(cursor.getRecord());
            }
          }
          catch (ChangelogException e)
          {
            logger.traceException(e);
          }
          finally
          {
            close(cursor);
          }
          /*
           * If the late queue is empty then we could not find any messages in
           * the replication log so the remote server is not late anymore.
           */
          fillLateQueue();
          if (lateQueue.isEmpty())
          {
            // we could not find any messages in the changelog
            // so the remote server is not late anymore.
            synchronized (msgQueue)
            {
              // Ensure we are below threshold so this server will follow the
@@ -330,8 +310,8 @@
          else
          {
            /*
             * if the first change in the lateQueue is also on the regular
             * queue, we can resume the processing from the regular queue
             * if the first change in the lateQueue is also on the regular queue,
             * we can resume the processing from the regular queue
             * -> set following to true and empty the lateQueue.
             */
            UpdateMsg msg = lateQueue.first();
@@ -353,7 +333,7 @@
        {
          // get the next change from the lateQueue
          UpdateMsg msg;
          synchronized (msgQueue)
          synchronized (msgQueue) // TODO JNR why synchronize(msgQueue) here?
          {
            msg = lateQueue.removeFirst();
          }
@@ -406,6 +386,27 @@
    return null;
  }
  private void fillLateQueue()
  {
    DBCursor<UpdateMsg> cursor = null;
    try
    {
      cursor = replicationServerDomain.getCursorFrom(serverState);
      while (cursor.next() && isLateQueueBelowThreshold())
      {
        lateQueue.add(cursor.getRecord());
      }
    }
    catch (ChangelogException e)
    {
      logger.traceException(e);
    }
    finally
    {
      close(cursor);
    }
  }
  private boolean isLateQueueBelowThreshold()
  {
    return lateQueue.count() < 100 && lateQueue.bytesCount() < 50000;
@@ -425,16 +426,14 @@
      {
        if (!msgQueue.isEmpty())
        {
          UpdateMsg msg = msgQueue.first();
          result = msg.getCSN();
          result = msgQueue.first().getCSN();
        }
      }
      else
      {
        if (!lateQueue.isEmpty())
        {
          UpdateMsg msg = lateQueue.first();
          result = msg.getCSN();
          result = lateQueue.first().getCSN();
        }
        else
        {
opendj3-server-dev/src/server/org/opends/server/replication/server/MsgQueue.java
@@ -26,7 +26,6 @@
 */
package org.opends.server.replication.server;
import java.util.NavigableMap;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import java.util.TreeMap;
@@ -38,13 +37,21 @@
/**
 * This class is used to build ordered lists of UpdateMsg.
 * The order is defined by the order of the CSN of the UpdateMsg.
 * @ThreadSafe
 */
public class MsgQueue
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  private NavigableMap<CSN, UpdateMsg> map = new TreeMap<CSN, UpdateMsg>();
  private TreeMap<CSN, UpdateMsg> map = new TreeMap<CSN, UpdateMsg>();
  /**
   * FIXME JNR to be investigated:
   * I strongly suspect that we could replace this field
   * by using the synchronized keyword on each method.
   * However, MessageHandler is weirdly synchronizing on msgQueue field
   * even though it is touching the lateQueue field (?!?).
   */
  private final Object lock = new Object();
  /** The total number of bytes for all the message in the queue. */
@@ -112,7 +119,7 @@
  {
    synchronized (lock)
    {
      UpdateMsg msgSameCSN = map.put(update.getCSN(), update);
      final UpdateMsg msgSameCSN = map.put(update.getCSN(), update);
      if (msgSameCSN != null)
      {
        try
@@ -123,11 +130,11 @@
          {
            // Adding 2 msgs with the same CSN is ok only when
            // the 2 msgs are the same
            bytesCount += (update.size() - msgSameCSN.size());
            bytesCount += update.size() - msgSameCSN.size();
            logger.error(ERR_RSQUEUE_DIFFERENT_MSGS_WITH_SAME_CN, msgSameCSN.getCSN(), msgSameCSN, update);
          }
        }
        catch(Exception e)
        catch (Exception e)
        {
          logger.traceException(e);
        }
@@ -149,10 +156,12 @@
  {
    synchronized (lock)
    {
      UpdateMsg update = map.get(map.firstKey());
      // FIXME JNR replace next 2 lines with just that one:
      // final UpdateMsg update = map.pollFirstEntry().getValue();
      final UpdateMsg update = map.get(map.firstKey());
      map.remove(update.getCSN());
      bytesCount -= update.size();
      if ((map.size() == 0) && (bytesCount != 0))
      if (map.isEmpty() && bytesCount != 0)
      {
        // should never happen
        logger.error(ERR_BYTE_COUNT, bytesCount);
@@ -197,18 +206,33 @@
   * message. If the passed in message is not contained in the current queue,
   * then all messages will be removed from it.
   *
   * @param msg
   * @param finalMsg
   *          the final message to reach when consuming messages from this queue
   */
  public void consumeUpTo(UpdateMsg msg)
  public void consumeUpTo(UpdateMsg finalMsg)
  {
    UpdateMsg msg1;
    // FIXME this code could be more efficient if the msgQueue could call the
    // following code (to be tested):
    // if (!map.containsKey(finalMsg.getCSN())) {
    // map.clear();
    // } else {
    // map.headMap(finalMsg.getCSN(), true).clear();
    // }
    final CSN finalCSN = finalMsg.getCSN();
    UpdateMsg msg;
    do
    {
      // FIXME this code could be more efficient if the msgQueue could call the
      // following code (to be tested):
      // map.headMap(msg.getCSN(), true).clear()
      msg1 = removeFirst();
    } while (!msg.getCSN().equals(msg1.getCSN()));
      msg = removeFirst();
    }
    while (!finalCSN.equals(msg.getCSN()));
  }
  /** {@inheritDoc} */
  @Override
  public String toString()
  {
    return getClass().getSimpleName() + " bytesCount=" + bytesCount + " queue="
        + map.values();
  }
}
opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/JEReplicaDBCursor.java
@@ -30,7 +30,7 @@
import org.opends.server.replication.protocol.UpdateMsg;
import org.opends.server.replication.server.changelog.api.ChangelogException;
import org.opends.server.replication.server.changelog.api.DBCursor;
import org.opends.server.replication.server.changelog.je.ReplicationDB.*;
import org.opends.server.replication.server.changelog.je.ReplicationDB.ReplServerDBCursor;
/**
 * Berkeley DB JE implementation of {@link DBCursor}.
@@ -89,11 +89,7 @@
    final ReplServerDBCursor localCursor = cursor;
    currentChange = localCursor != null ? localCursor.next() : null;
    if (currentChange != null)
    {
      lastNonNullCurrentCSN = currentChange.getCSN();
    }
    else
    if (currentChange == null)
    {
      synchronized (this)
      {
@@ -105,13 +101,14 @@
        // and fixing such issue with unit tests.
        cursor = db.openReadCursor(lastNonNullCurrentCSN);
        currentChange = cursor.next();
        if (currentChange != null)
        {
          lastNonNullCurrentCSN = currentChange.getCSN();
        }
      }
    }
    return currentChange != null;
    if (currentChange != null)
    {
      lastNonNullCurrentCSN = currentChange.getCSN();
      return true;
    }
    return false;
  }
  /** {@inheritDoc} */
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/monitors/InternalSearchMonitorTestCase.java
@@ -26,28 +26,30 @@
 */
package org.opends.server.monitors;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import org.forgerock.opendj.ldap.SearchScope;
import org.opends.server.types.DN;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.TestCaseUtils;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.types.SearchResultEntry;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import static org.forgerock.opendj.ldap.SearchScope.*;
import static org.opends.server.protocols.internal.InternalClientConnection.*;
import static org.opends.server.types.SearchFilter.*;
import static org.testng.Assert.*;
/**
 * Interacts with the Directory Server monitor providers by retrieving the
 * monitor entries with internal searches.
 */
@SuppressWarnings("javadoc")
public class InternalSearchMonitorTestCase
       extends MonitorTestCase
{
@@ -55,20 +57,15 @@
  /**
   * Ensures that the Directory Server is started.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass()
  public void startServer()
         throws Exception
  @BeforeClass
  public void startServer() throws Exception
  {
    TestCaseUtils.startServer();
    DirectoryServer.registerMonitorProvider(testMonitorProvider);
  }
  @AfterClass()
  @AfterClass
  public void deregisterTestMonitor()
  {
    DirectoryServer.deregisterMonitorProvider(testMonitorProvider);
@@ -76,20 +73,14 @@
  /**
   * Uses an internal subtree search to retrieve the monitor entries.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test
  public void testWithSubtreeMonitorSearch()
         throws Exception
  public void testWithSubtreeMonitorSearch() throws Exception
  {
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(DN.valueOf("cn=monitor"), SearchScope.WHOLE_SUBTREE,
              SearchFilter.createFilterFromString("(objectClass=*)"));
    assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS,
        "Failed to search cn=monitor subtree. Got error message: " + searchOperation.getErrorMessage());
    InternalSearchOperation op = getRootConnection().processSearch(
        "cn=monitor", WHOLE_SUBTREE, "(objectClass=*)");
    assertEquals(op.getResultCode(), ResultCode.SUCCESS,
        "Failed to search cn=monitor subtree. Got error message: " + op.getErrorMessage());
  }
@@ -102,18 +93,14 @@
  @DataProvider(name = "monitorNames")
  public Object[][] getMonitorNames()
  {
    ArrayList<String> monitorNames = new ArrayList<String>();
    for (String name : DirectoryServer.getMonitorProviders().keySet())
    {
      monitorNames.add(name);
    }
    Set<String> monitorNames = DirectoryServer.getMonitorProviders().keySet();
    Iterator<String> it = monitorNames.iterator();
    Object[][] nameArray = new Object[monitorNames.size()][1];
    for (int i=0; i < nameArray.length; i++)
    {
      nameArray[i] = new Object[] { monitorNames.get(i) };
      nameArray[i] = new Object[] { it.next() };
    }
    return nameArray;
  }
@@ -123,55 +110,39 @@
   * Uses a set of internal base-level searches to retrieve the monitor entries.
   *
   * @param  monitorName  The name of the monitor entry to retrieve.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "monitorNames")
  public void testWithBaseObjectMonitorSearch(String monitorName)
         throws Exception
  public void testWithBaseObjectMonitorSearch(String monitorName) throws Exception
  {
    // could be more than one level
    DN monitorDN = DN.valueOf("cn="+monitorName+",cn=monitor");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(monitorDN,
              SearchScope.BASE_OBJECT,
              SearchFilter.createFilterFromString("(objectClass=*)"));
    assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS,
        "Failed to read " + monitorDN + " entry. Got error message: " + searchOperation.getErrorMessage());
    final String monitorDN = "cn="+monitorName+",cn=monitor";
    InternalSearchOperation op = getRootConnection().processSearch(
        monitorDN, BASE_OBJECT, "(objectClass=*)");
    assertEquals(op.getResultCode(), ResultCode.SUCCESS,
        "Failed to read " + monitorDN + " entry. Got error message: " + op.getErrorMessage());
  }
  /**
   * Uses an internal subtree search to retrieve the monitor entries, then
   * verifies that the resulting entry DNs can be used to get the same
   * entries with a base object search.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test
  public void testWithSubtreeAndBaseMonitorSearch()
         throws Exception
  public void testWithSubtreeAndBaseMonitorSearch() throws Exception
  {
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(DN.valueOf("cn=monitor"), SearchScope.WHOLE_SUBTREE,
              SearchFilter.createFilterFromString("(objectClass=*)"));
    assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS,
        "Failed to search cn=monitor subtree. Got error message: " + searchOperation.getErrorMessage());
    final InternalClientConnection conn = getRootConnection();
    InternalSearchOperation op = conn.processSearch(
        "cn=monitor", WHOLE_SUBTREE, "(objectClass=*)");
    assertEquals(op.getResultCode(), ResultCode.SUCCESS,
        "Failed to search cn=monitor subtree. Got error message: " + op.getErrorMessage());
    for (SearchResultEntry sre : searchOperation.getSearchEntries())
    for (SearchResultEntry sre : op.getSearchEntries())
    {
      SearchFilter filter =
           SearchFilter.createFilterFromString("(objectClass=*)");
      InternalSearchOperation readOperation =
           conn.processSearch(sre.getName(), SearchScope.BASE_OBJECT, filter);
      assertEquals(readOperation.getResultCode(), ResultCode.SUCCESS,
          "Failed to read " + sre.getName() + " entry. Got error message: " + readOperation.getErrorMessage());
      final InternalSearchOperation readOp = conn.processSearch(
          sre.getName(), BASE_OBJECT, createFilterFromString("(objectClass=*)"));
      assertEquals(readOp.getResultCode(), ResultCode.SUCCESS,
          "Failed to read " + sre.getName() + " entry. Got error message: " + readOp.getErrorMessage());
    }
  }
}
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ExternalChangeLogTest.java
@@ -28,17 +28,13 @@
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.net.Socket;
import java.util.*;
import org.assertj.core.api.Assertions;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.*;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.server.ExternalChangelogDomainCfg;
import org.opends.server.api.Backend;
@@ -67,7 +63,12 @@
import org.opends.server.tools.LDAPSearch;
import org.opends.server.tools.LDAPWriter;
import org.opends.server.types.*;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.types.Attribute;
import org.opends.server.types.Attributes;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
import org.opends.server.types.RDN;
import org.opends.server.util.LDIFWriter;
import org.opends.server.util.TimeThread;
import org.opends.server.workflowelement.externalchangelog.ECLSearchOperation;
@@ -79,11 +80,11 @@
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.*;
import static org.forgerock.opendj.ldap.ResultCode.*;
import static org.opends.messages.ReplicationMessages.*;
import static org.opends.server.TestCaseUtils.*;
import static org.opends.server.controls.PersistentSearchChangeType.*;
import static org.opends.server.replication.protocol.OperationContext.*;
import static org.forgerock.opendj.ldap.ResultCode.*;
import static org.opends.server.util.StaticUtils.*;
import static org.testng.Assert.*;
@@ -163,17 +164,7 @@
    debugInfo("configure", "ReplicationServer created"+replicationServer);
  }
  /**
   * Launcher.
   */
  @Test(enabled=true)
  public void PreTest() throws Exception
  {
    // No RSDomain created yet => RS only case => ECL is not a supported
    ECLIsNotASupportedSuffix();
  }
  @Test(enabled=true, dependsOnMethods = { "PreTest"})
  @Test(enabled = true, dependsOnMethods = { "TestECLIsNotASupportedSuffix" })
  public void PrimaryTest() throws Exception
  {
    replicationServer.getChangelogDB().setPurgeDelay(0);
@@ -194,27 +185,6 @@
    ECLCompatTestLimits(1,4,true);
  }
  @Test(enabled=false, dependsOnMethods = { "PrimaryTest"})
  public void TestWithTwoDomains() throws Exception
  {
    replicationServer.getChangelogDB().setPurgeDelay(0);
    // Test with a mix of domains, a mix of DSes
    ECLTwoDomains();
  }
  @Test(enabled=false, dependsOnMethods = { "PrimaryTest"})
  public void TestAfterChangelogTrim() throws Exception
  {
    // Test ECL after changelog trimming
    ECLAfterChangelogTrim();
  }
  @Test(enabled=true, dependsOnMethods = { "PrimaryTest"})
  public void TestAfterDomainIsRemoved() throws Exception
  {
    ECLAfterDomainIsRemoved();
  }
  @Test(enabled=true, dependsOnMethods = { "PrimaryTest"})
  public void TestWithAndWithoutControl() throws Exception
  {
@@ -228,27 +198,6 @@
    ECLCompatWriteReadAllOps(5);
  }
  @Test(enabled = true, dependsOnMethods = { "TestWithAndWithoutControl" })
  public void TestWithIncludeAttributes() throws Exception
  {
    ECLIncludeAttributes();
  }
  @Test(enabled=true, dependsOnMethods = { "PrimaryTest"})
  public void TestChangeTimeHeartBeat() throws Exception
  {
    ChangeTimeHeartbeatTest();
  }
  @Test(enabled=true, dependsOnMethods = { "PrimaryTest"})
  public void TestOperationalAttributesNotVisibleOutsideRootDSE() throws Exception
  {
    // Test that ECL Operational, virtual attributes are not visible
    // outside rootDSE. Next test will test access in RootDSE.
    // This one checks in data.
    ECLOperationalAttributesFailTest();
  }
  @Test(enabled=false, dependsOnMethods = { "PrimaryTest"})
  public void PrimaryFullTest() throws Exception
  {
@@ -281,10 +230,8 @@
    // Test all types of ops.
    ECLAllOps(); // Do not clean the db for the next test
    // Test that ECL Operational, virtual attributes are not visible
    // outside rootDSE. Next test will test access in RootDSE.
    // This one checks in data.
    ECLOperationalAttributesFailTest();
    // Test after this one will test access in RootDSE. This one checks in data.
    TestECLOperationalAttributesNotVisibleOutsideRootDSE();
    // First and last should be ok whenever a request has been done or not
    // in compat mode.
@@ -298,27 +245,20 @@
    ECLRemoteNonEmpty();
  }
  /** Persistent search with changesOnly request */
  @Test(enabled=false, groups="slow", dependsOnMethods = { "PrimaryTest"})
  public void FullTestPersistentSearchWithChangesOnlyRequest() throws Exception
  {
    // Persistent search with changesOnly request
    ECLPsearch(true, false);
  }
  /** Persistent search with init values request */
  @Test(enabled=false, groups="slow", dependsOnMethods = { "PrimaryTest"})
  public void FullTestPersistentSearchWithInitValuesRequest() throws Exception
  {
    // Persistent search with init values request
    ECLPsearch(false, false);
  }
  @Test(enabled=false, groups="slow", dependsOnMethods = { "PrimaryTest"})
  public void FullTestSimultaneousPersistentSearches() throws Exception
  {
    // Simultaneous psearches
    ECLSimultaneousPsearches();
  }
  // TODO:ECL Test SEARCH abandon and check everything shutdown and cleaned
  // TODO:ECL Test PSEARCH abandon and check everything shutdown and cleaned
  // TODO:ECL Test invalid DN in cookie returns UNWILLING + message
@@ -412,7 +352,9 @@
    assertEquals(ico.getErrorMessage().toMessage(), NOTE_SEARCH_CHANGELOG_INSUFFICIENT_PRIVILEGES.get());
  }
  private void ECLIsNotASupportedSuffix() throws Exception
  /** No RSDomain created yet => RS only case => ECL is not a supported. */
  @Test(enabled = true)
  public void TestECLIsNotASupportedSuffix() throws Exception
  {
    ECLCompatTestLimits(0,0, false);
  }
@@ -472,11 +414,15 @@
  }
  /**
   * Objectives
   *   - Test that everything is ok with changes on 2 suffixes
   * Procedure
   *   - From 1 remote ECL session,
   *   - Test simple update to be received from 2 suffixes
   * Objectives:
   * <ul>
   * <li>Test that everything is ok with changes on 2 suffixes</li>
   * </ul>
   * Procedure:
   * <ul>
   * <li>From 1 remote ECL session,</li>
   * <li>Test simple update to be received from 2 suffixes</li>
   * </ul>
   */
  private void ECLRemoteNonEmpty() throws Exception
  {
@@ -560,7 +506,7 @@
    debugInfo(tn, "Starting test\n\n");
    // root entry returned
    searchOnChangelog("(objectclass=*)", Collections.<String> emptySet(), createControls(""),
    searchOnChangelog("(objectclass=*)", Collections.<String> emptySet(), createCookieControl(""),
        1, ResultCode.SUCCESS, tn);
    debugInfo(tn, "Ending test successfully");
@@ -571,12 +517,11 @@
   * @param cookie The provided cookie.
   * @return The built list of controls.
   */
  private List<Control> createControls(String cookie) throws DirectoryException
  private List<Control> createCookieControl(String cookie) throws DirectoryException
  {
    final MultiDomainServerState state = new MultiDomainServerState(cookie);
    final List<Control> controls = new ArrayList<Control>(1);
    controls.add(new ExternalChangelogRequestControl(true, state));
    return controls;
    final Control cookieControl = new ExternalChangelogRequestControl(true, state);
    return newList(cookieControl);
  }
  /**
@@ -668,11 +613,16 @@
  }
  /**
   * From embedded ECL Search ECL with 4 messages on 2 suffixes from 2 brokers
   * From embedded ECL Search ECL with 4 messages on 2 suffixes from 2 brokers.
   * Test with a mix of domains, a mix of DSes.
   */
  private void ECLTwoDomains() throws Exception
  @Test(enabled=false, dependsOnMethods = { "PrimaryTest"})
  public void TestECLWithTwoDomains() throws Exception
  {
    String tn = "ECLTwoDomains";
    replicationServer.getChangelogDB().setPurgeDelay(0);
    String tn = "TestECLWithTwoDomains";
    debugInfo(tn, "Starting test");
    ReplicationBroker s1test = null;
@@ -882,7 +832,7 @@
      throws Exception
  {
    debugInfo(testName, "Search with cookie=[" + cookie + "] filter=[" + filterString + "]");
    return searchOnChangelog(filterString, ALL_ATTRIBUTES, createControls(cookie),
    return searchOnChangelog(filterString, ALL_ATTRIBUTES, createCookieControl(cookie),
        expectedNbEntries, expectedResultCode, testName);
  }
@@ -927,9 +877,10 @@
  }
  /** Test ECL content after replication changelogDB trimming */
  private void ECLAfterChangelogTrim() throws Exception
  @Test(enabled=false, dependsOnMethods = { "PrimaryTest"})
  public void testECLAfterChangelogTrim() throws Exception
  {
    String testName = "ECLAfterChangelogTrim";
    String testName = "testECLAfterChangelogTrim";
    debugInfo(testName, "Starting test");
    ReplicationBroker server01 = null;
@@ -994,9 +945,10 @@
  }
  /** Test ECL content after a domain has been removed. */
  private void ECLAfterDomainIsRemoved() throws Exception
  @Test(enabled=true, dependsOnMethods = { "PrimaryTest"})
  public void testECLAfterDomainIsRemoved() throws Exception
  {
    String testName = "ECLAfterDomainIsRemoved";
    String testName = "testECLAfterDomainIsRemoved";
    debugInfo(testName, "Starting test");
    ReplicationBroker server01 = null;
@@ -1133,7 +1085,7 @@
          baseUUID,
          entry.getObjectClassAttribute(),
          entry.getAttributes(),
          new ArrayList<Attribute>());
          Collections.<Attribute> emptyList());
      server01.publish(addMsg);
      debugInfo(tn, " publishes " + addMsg.getCSN());
@@ -1250,31 +1202,27 @@
    assertThat(actualDN).isEqualToIgnoringCase(expectedDN);
  }
  private List<String> getControls(String resultString)
  private List<String> getControls(String resultString) throws Exception
  {
    StringReader r=new StringReader(resultString);
    BufferedReader br=new BufferedReader(r);
    List<String> ctrlList = new ArrayList<String>();
    try {
      while(true) {
        String s = br.readLine();
        if(s == null)
        {
          break;
        }
        if(!s.startsWith("#"))
        {
          continue;
        }
        String[] a=s.split(": ");
        if(a.length != 2)
        {
          break;
        }
        ctrlList.add(a[1]);
    final BufferedReader br = new BufferedReader(new StringReader(resultString));
    final List<String> ctrlList = new ArrayList<String>();
    while (true)
    {
      final String s = br.readLine();
      if (s == null)
      {
        break;
      }
    } catch (IOException e) {
      assertEquals(0, 1, e.getMessage());
      if (!s.startsWith("#"))
      {
        continue;
      }
      final String[] a = s.split(": ");
      if (a.length != 2)
      {
        break;
      }
      ctrlList.add(a[1]);
    }
    return ctrlList;
  }
@@ -1433,7 +1381,7 @@
      // Creates cookie control
      String cookie = "";
      List<Control> controls = createControls(cookie);
      List<Control> controls = createCookieControl(cookie);
      if (compatMode)
      {
        cookie = null;
@@ -1575,7 +1523,7 @@
            createSearchRequest("(targetDN=*directpsearch*,o=test)", null);
        debugInfo(tn, "ACI test : sending search");
        message = new LDAPMessage(2, searchRequest, createControls(""));
        message = new LDAPMessage(2, searchRequest, createCookieControl(""));
        w.writeMessage(message);
        searchesDone=0;
@@ -1654,11 +1602,12 @@
  }
  /**
   * Test parallel simultaneous psearch with different filters.
   * Test parallel simultaneous persistent search with different filters.
   */
  private void ECLSimultaneousPsearches() throws Exception
  @Test(enabled = false, groups = "slow", dependsOnMethods = { "PrimaryTest" })
  public void FullTestSimultaneousPersistentSearches() throws Exception
  {
    String tn = "ECLSimultaneousPsearches";
    String tn = "FullTestSimultaneousPersistentSearches";
    debugInfo(tn, "Starting test \n\n");
    Socket s1 = null, s2 = null, s3 = null;
    ReplicationBroker server01 = null;
@@ -1724,7 +1673,7 @@
      // Creates cookie control
      String cookie = "";
      List<Control> controls = createControls(cookie);
      List<Control> controls = createCookieControl(cookie);
      if (compatMode)
      {
        cookie = null;
@@ -2163,9 +2112,10 @@
  /**
   * FIXME this test actually tests nothing: there are no asserts.
   */
  private void ChangeTimeHeartbeatTest() throws Exception
  @Test(enabled = true, dependsOnMethods = { "PrimaryTest" })
  public void testChangeTimeHeartbeat() throws Exception
  {
    String tn = "ChangeTimeHeartbeatTest";
    String tn = "testChangeTimeHeartbeat";
    debugInfo(tn, "Starting test");
    ReplicationBroker s1test = null;
    ReplicationBroker s2test = null;
@@ -2292,7 +2242,7 @@
          baseUUID,
          entry.getObjectClassAttribute(),
          entry.getAttributes(),
          new ArrayList<Attribute>());
          Collections.<Attribute> emptyList());
      server01.publish(addMsg);
      debugInfo(tn, " publishes " + addMsg.getCSN());
@@ -2604,9 +2554,13 @@
    debugInfo(tn, "Ending test with success");
  }
  private void ECLOperationalAttributesFailTest() throws Exception
  /**
   * Test that ECL Operational, virtual attributes are not visible outside rootDSE.
   */
  @Test(enabled = true, dependsOnMethods = { "PrimaryTest" })
  public void TestECLOperationalAttributesNotVisibleOutsideRootDSE() throws Exception
  {
    String tn = "ECLOperationalAttributesFailTest";
    String tn = "TestECLOperationalAttributesNotVisibleOutsideRootDSE";
    // The goal is to verify that the Changelog attributes are not
    // available in other entries. We u
    debugInfo(tn, "Starting test \n\n");
@@ -2623,9 +2577,7 @@
            0, // Time limit
            false, // Types only
            "(objectclass=*)",
            attributes,
            NO_CONTROL,
            null);
            attributes);
    waitOpResult(searchOp, ResultCode.SUCCESS);
    final List<SearchResultEntry> entries = searchOp.getSearchEntries();
@@ -2721,9 +2673,7 @@
        0, // Time limit
        false, // Types only
        "(objectclass=*)",
        attributes,
        NO_CONTROL,
        null);
        attributes);
    waitOpResult(searchOp, ResultCode.SUCCESS);
    return searchOp;
  }
@@ -2774,11 +2724,12 @@
  }
  /**
   * Test ECl entry attributes, and there configuration.
   * Test ECl entry attributes, and their configuration.
   */
  private void ECLIncludeAttributes() throws Exception
  @Test(enabled = true, dependsOnMethods = { "TestWithAndWithoutControl" })
  public void TestECLWithIncludeAttributes() throws Exception
  {
    String tn = "ECLIncludeAttributes";
    String tn = "TestECLWithIncludeAttributes";
    debugInfo(tn, "Starting test\n\n");
    final String backendId3 = "test3";
@@ -2953,9 +2904,7 @@
  private List<Modification> createMods(String attributeName, String valueString)
  {
    Attribute attr = Attributes.create(attributeName, valueString);
    List<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE, attr));
    return mods;
    return newList(new Modification(ModificationType.REPLACE, attr));
  }
  private Entry parseIncludedAttributes(SearchResultEntry resultEntry,
@@ -2970,8 +2919,7 @@
    return TestCaseUtils.makeEntry(ldif);
  }
  private void waitOpResult(Operation operation, ResultCode expectedResult)
      throws Exception
  private void waitOpResult(Operation operation, ResultCode expectedResult) throws Exception
  {
    int i = 0;
    while (operation.getResultCode() == ResultCode.UNDEFINED