From 00226e83ae4599836caaaf50e50c264905f12ab4 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 22 Feb 2011 14:46:38 +0000
Subject: [PATCH] Fixed issue OPENDJ-50: ECL base object search operations on cn=changelog take a long time if the change log is big  https://bugster.forgerock.org/jira/browse/OPENDJ-50

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java |  116 ++++++++------
 opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java      |  357 ++++++++++++++++++++++++-------------------
 2 files changed, 262 insertions(+), 211 deletions(-)

diff --git a/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java b/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
index e2fb03f..5f70266 100644
--- a/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
@@ -87,27 +87,7 @@
 import org.opends.server.replication.protocol.StartECLSessionMsg;
 import org.opends.server.replication.protocol.UpdateMsg;
 import org.opends.server.replication.server.ReplicationServer;
-import org.opends.server.types.Attribute;
-import org.opends.server.types.AttributeBuilder;
-import org.opends.server.types.AttributeType;
-import org.opends.server.types.AttributeValue;
-import org.opends.server.types.Attributes;
-import org.opends.server.types.CancelRequest;
-import org.opends.server.types.CancelResult;
-import org.opends.server.types.CanceledOperationException;
-import org.opends.server.types.Control;
-import org.opends.server.types.DN;
-import org.opends.server.types.DebugLogLevel;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.Entry;
-import org.opends.server.types.FilterType;
-import org.opends.server.types.Modification;
-import org.opends.server.types.ObjectClass;
-import org.opends.server.types.Privilege;
-import org.opends.server.types.RawAttribute;
-import org.opends.server.types.ResultCode;
-import org.opends.server.types.SearchFilter;
-import org.opends.server.types.SearchScope;
+import org.opends.server.types.*;
 import org.opends.server.types.operation.PostOperationSearchOperation;
 import org.opends.server.types.operation.PreOperationSearchOperation;
 import org.opends.server.types.operation.SearchEntrySearchOperation;
@@ -137,11 +117,60 @@
    */
   private StartECLSessionMsg startECLSessionMsg;
 
+  //The set of supported controls for this WE
+  private static final HashSet<String> CHANGELOG_SUPPORTED_CONTROLS;
+  static
+  {
+    CHANGELOG_SUPPORTED_CONTROLS = new HashSet<String>(0);
+    CHANGELOG_SUPPORTED_CONTROLS
+        .add(ServerConstants.OID_SERVER_SIDE_SORT_REQUEST_CONTROL);
+    CHANGELOG_SUPPORTED_CONTROLS.add(ServerConstants.OID_VLV_REQUEST_CONTROL);
+  }
+
+
+  // The set of objectclasses that will be used in ECL root entry.
+  private static final HashMap<ObjectClass, String>
+    CHANGELOG_ROOT_OBJECT_CLASSES;
+  static
+  {
+    CHANGELOG_ROOT_OBJECT_CLASSES = new LinkedHashMap<ObjectClass, String>(2);
+
+    ObjectClass topOC = DirectoryServer.getObjectClass(OC_TOP, true);
+    CHANGELOG_ROOT_OBJECT_CLASSES.put(topOC, OC_TOP);
+
+    ObjectClass containerOC = DirectoryServer.getObjectClass("container", true);
+    CHANGELOG_ROOT_OBJECT_CLASSES.put(containerOC, "container");
+  }
+
   // The set of objectclasses that will be used in ECL entries.
-  private static HashMap<ObjectClass,String> eclObjectClasses;
+  private static final HashMap<ObjectClass, String>
+    CHANGELOG_ENTRY_OBJECT_CLASSES;
+  static
+  {
+    CHANGELOG_ENTRY_OBJECT_CLASSES = new LinkedHashMap<ObjectClass, String>(2);
+
+    ObjectClass topOC = DirectoryServer.getObjectClass(OC_TOP, true);
+    CHANGELOG_ENTRY_OBJECT_CLASSES.put(topOC, OC_TOP);
+
+    ObjectClass eclEntryOC = DirectoryServer.getObjectClass(OC_CHANGELOG_ENTRY,
+        true);
+    CHANGELOG_ENTRY_OBJECT_CLASSES.put(eclEntryOC, OC_CHANGELOG_ENTRY);
+  }
 
   // The associated DN.
-  private DN rootBaseDN;
+  private static final DN CHANGELOG_ROOT_DN;
+  static
+  {
+    try
+    {
+      CHANGELOG_ROOT_DN = DN
+          .decode(ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT);
+    }
+    catch (Exception e)
+    {
+      throw new RuntimeException(e);
+    }
+  }
 
   /**
    * The replication server in which the search on ECL is to be performed.
@@ -170,9 +199,6 @@
 
   private ExternalChangeLogSession eclSession;
 
-  // The set of supported controls for this WE
-  private HashSet<String> supportedControls;
-
   /**
    * Creates a new operation that may be used to search for entries in a local
    * backend of the Directory Server.
@@ -183,27 +209,6 @@
   {
     super(search);
 
-    try
-    {
-      rootBaseDN = DN.decode(ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT);
-    }
-    catch (Exception e){}
-
-    // Construct the set of objectclasses to include in the base monitor entry.
-    eclObjectClasses = new LinkedHashMap<ObjectClass,String>(2);
-    ObjectClass topOC = DirectoryServer.getObjectClass(OC_TOP, true);
-    eclObjectClasses.put(topOC, OC_TOP);
-    ObjectClass eclEntryOC = DirectoryServer.getObjectClass(OC_CHANGELOG_ENTRY,
-        true);
-    eclObjectClasses.put(eclEntryOC, OC_CHANGELOG_ENTRY);
-
-
-    // Define an empty sets for the supported controls and features.
-    // FIXME:ECL Decide if ServerSideControl and VLV are supported
-    supportedControls = new HashSet<String>(0);
-    supportedControls.add(ServerConstants.OID_SERVER_SIDE_SORT_REQUEST_CONTROL);
-    supportedControls.add(ServerConstants.OID_VLV_REQUEST_CONTROL);
-
     ECLWorkflowElement.attachLocalOperation(search, this);
   }
 
@@ -251,15 +256,6 @@
         excludedDomains.add(ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT);
       startECLSessionMsg.setExcludedDNs(excludedDomains);
 
-      // Test existence of the RS - normally should always be here
-      if (replicationServer == null)
-      {
-        setResultCode(ResultCode.OPERATIONS_ERROR);
-        appendErrorMessage(ERR_SEARCH_BASE_DOESNT_EXIST.get(
-            String.valueOf(baseDN)));
-        break searchProcessing;
-      }
-
       // Process the search base and filter to convert them from their raw forms
       // as provided by the client to the forms required for the rest of the
       // search processing.
@@ -269,6 +265,15 @@
         break searchProcessing;
       }
 
+      // Test existence of the RS - normally should always be here
+      if (replicationServer == null)
+      {
+        setResultCode(ResultCode.OPERATIONS_ERROR);
+        appendErrorMessage(ERR_SEARCH_BASE_DOESNT_EXIST.get(
+            String.valueOf(baseDN)));
+        break searchProcessing;
+      }
+
       // Analyse controls - including the cookie control
       try
       {
@@ -282,10 +287,10 @@
         break searchProcessing;
       }
 
-      // Process filter - extract draft change number (seqnum) conditions
+      // Process search parameters to optimize session query.
       try
       {
-        evaluateFilter(startECLSessionMsg, this.getFilter());
+        evaluateSearchParameters(startECLSessionMsg, baseDN, filter);
       }
       catch (DirectoryException de)
       {
@@ -437,6 +442,7 @@
             Entry entry;
             try
             {
+              // FIXME: this is broken (recursive)?
               entry = DirectoryServer.getEntry(baseDN);
             }
             catch (DirectoryException de)
@@ -608,10 +614,10 @@
       ECLUpdateMsg update = eclSession.getNextUpdate();
 
       // Return root entry if requested.
-      if (!getScope().equals(SearchScope.SINGLE_LEVEL))
+      if (CHANGELOG_ROOT_DN.matchesBaseAndScope(baseDN, getScope()))
       {
-        Entry entry = createRootEntry(update != null);
-        if (matchFilter(entry))
+        final Entry entry = createRootEntry(update != null);
+        if (filter.matchesEntry(entry))
         {
           if (!returnEntry(entry, null))
           {
@@ -622,22 +628,28 @@
         }
       }
 
-      if (!getScope().equals(SearchScope.BASE_OBJECT))
+      if (baseDN.equals(CHANGELOG_ROOT_DN) && getScope().equals(
+          SearchScope.BASE_OBJECT))
       {
-        while (update != null)
+        // Only the change log root entry was requested. There is no need to
+        // process other entries.
+        return;
+      }
+
+      // Process change log entries.
+      while (update != null)
+      {
+        // Check for a request to cancel this operation.
+        checkIfCanceled(false);
+
+        if (!buildAndReturnEntry(update))
         {
-          // Check for a request to cancel this operation.
-          checkIfCanceled(false);
-
-          if (!buildAndReturnEntry(update))
-          {
-            // Abandon, Size limit reached.
-            abortECLSession = true;
-            return;
-          }
-
-          update = eclSession.getNextUpdate();
+          // Abandon, Size limit reached.
+          abortECLSession = true;
+          return;
         }
+
+        update = eclSession.getNextUpdate();
       }
     }
     catch (CanceledOperationException e)
@@ -661,8 +673,7 @@
 
   private boolean supportsControl(String oid)
   {
-    return ((supportedControls != null) &&
-        supportedControls.contains(oid));
+    return CHANGELOG_SUPPORTED_CONTROLS.contains(oid);
   }
 
   /**
@@ -675,37 +686,42 @@
    * @throws DirectoryException When an errors occurs.
    */
   private boolean buildAndReturnEntry(ECLUpdateMsg eclmsg)
-  throws DirectoryException
+      throws DirectoryException
   {
-    Entry entry = null;
-
-    // build and filter
-    entry = createEntryFromMsg(eclmsg);
-    if (matchFilter(entry))
+    final Entry entry = createEntryFromMsg(eclmsg);
+    if (matchScopeAndFilter(entry))
     {
-      List<Control> controls = new ArrayList<Control>(0);
-
-      EntryChangelogNotificationControl clrc
-      = new EntryChangelogNotificationControl(
-          true,eclmsg.getCookie().toString());
+      List<Control> controls = new ArrayList<Control>(1);
+      EntryChangelogNotificationControl clrc =
+        new EntryChangelogNotificationControl(
+            true, eclmsg.getCookie().toString());
       controls.add(clrc);
       return returnEntry(entry, controls);
     }
     return true;
   }
 
+
+
   /**
    * Test if the provided entry matches the filter, base and scope.
-   * @param  entry The provided entry
+   *
+   * @param entry
+   *          The provided entry
    * @return whether the entry matches.
-   * @throws DirectoryException When a problem occurs.
+   * @throws DirectoryException
+   *           When a problem occurs.
    */
-  private boolean matchFilter(Entry entry)
-  throws DirectoryException
+  private boolean matchScopeAndFilter(Entry entry) throws DirectoryException
   {
-    boolean baseScopeMatch = entry.matchesBaseAndScope(getBaseDN(), getScope());
-    boolean filterMatch = getFilter().matchesEntry(entry);
-    return (baseScopeMatch && filterMatch);
+    if (entry.matchesBaseAndScope(getBaseDN(), getScope()))
+    {
+      return getFilter().matchesEntry(entry);
+    }
+    else
+    {
+      return false;
+    }
   }
 
   /**
@@ -720,7 +736,7 @@
   {
     Entry clEntry = null;
 
-    // Get the meat fro the ecl msg
+    // Get the meat from the ecl msg
     UpdateMsg msg = eclmsg.getUpdateMsg();
 
     if (msg instanceof AddMsg)
@@ -857,18 +873,6 @@
    */
   private Entry createRootEntry(boolean hasSubordinates)
   {
-    HashMap<ObjectClass,String> oclasses =
-      new LinkedHashMap<ObjectClass,String>(3);
-
-    // Objectclass
-    HashMap<ObjectClass,String> rootObjectClasses =
-      new LinkedHashMap<ObjectClass,String>(2);
-    ObjectClass topOC = DirectoryServer.getObjectClass(OC_TOP, true);
-    rootObjectClasses.put(topOC, OC_TOP);
-    ObjectClass containerOC = DirectoryServer.getObjectClass("container", true);
-    rootObjectClasses.put(containerOC, "container");
-    oclasses.putAll(rootObjectClasses);
-
     // Attributes.
     HashMap<AttributeType,List<Attribute>> userAttrs =
       new LinkedHashMap<AttributeType,List<Attribute>>();
@@ -897,31 +901,28 @@
     // TODO:numSubordinates
 
     // hasSubordinates
-    if (hasSubordinates)
-    {
-      aType = DirectoryServer.getAttributeType("hassubordinates");
-      if (aType == null)
-        aType = DirectoryServer.getDefaultAttributeType("hasSubordinates");
-      a = Attributes.create("hasSubordinates", "true");
-      attrList = Collections.singletonList(a);
-      if (aType.isOperational())
-        operationalAttrs.put(aType, attrList);
-      else
-        userAttrs.put(aType, attrList);
-    }
+    aType = DirectoryServer.getAttributeType("hassubordinates");
+    if (aType == null)
+      aType = DirectoryServer.getDefaultAttributeType("hasSubordinates");
+    a = Attributes.create("hasSubordinates", Boolean.toString(hasSubordinates));
+    attrList = Collections.singletonList(a);
+    if (aType.isOperational())
+      operationalAttrs.put(aType, attrList);
+    else userAttrs.put(aType, attrList);
 
     // entryDN
     aType = DirectoryServer.getAttributeType("entrydn");
     if (aType == null)
       aType = DirectoryServer.getDefaultAttributeType("entryDN");
-    a = Attributes.create("entryDN", rootBaseDN.toNormalizedString());
+    a = Attributes.create("entryDN", CHANGELOG_ROOT_DN.toNormalizedString());
     attrList = Collections.singletonList(a);
     if (aType.isOperational())
       operationalAttrs.put(aType, attrList);
     else
       userAttrs.put(aType, attrList);
 
-    Entry e = new Entry(this.rootBaseDN, oclasses, userAttrs, operationalAttrs);
+    Entry e = new Entry(CHANGELOG_ROOT_DN, CHANGELOG_ROOT_OBJECT_CLASSES,
+        userAttrs, operationalAttrs);
     return e;
   }
 
@@ -965,25 +966,17 @@
     if (draftChangenumber == 0)
     {
       // Draft uncompat mode
-      dnString = "replicationcsn="+ changeNumber +"," + serviceID
-      + "," + ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT;
+      dnString = "replicationCSN=" + changeNumber + "," + serviceID + ","
+          + ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT;
     }
     else
     {
       // Draft compat mode
-      dnString = "changenumber="+ draftChangenumber + "," +
-      ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT;
+      dnString = "changeNumber=" + draftChangenumber + ","
+          + ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT;
     }
 
     // Objectclass
-    HashMap<ObjectClass,String> oClasses =
-      new LinkedHashMap<ObjectClass,String>(3);
-    oClasses.putAll(eclObjectClasses);
-
-    ObjectClass extensibleObjectOC =
-      DirectoryServer.getObjectClass(OC_EXTENSIBLE_OBJECT_LC, true);
-    oClasses.put(extensibleObjectOC, OC_EXTENSIBLE_OBJECT);
-
     HashMap<AttributeType,List<Attribute>> uAttrs =
       new LinkedHashMap<AttributeType,List<Attribute>>();
 
@@ -995,8 +988,9 @@
     // subSchemaSubentry
     aType = DirectoryServer.getAttributeType(ATTR_SUBSCHEMA_SUBENTRY_LC);
     if (aType == null)
-    aType = DirectoryServer.getDefaultAttributeType(ATTR_SUBSCHEMA_SUBENTRY_LC);
-    Attribute a = Attributes.create(ATTR_SUBSCHEMA_SUBENTRY_LC,
+      aType = DirectoryServer
+          .getDefaultAttributeType(ATTR_SUBSCHEMA_SUBENTRY_LC);
+    Attribute a = Attributes.create(aType,
         ConfigConstants.DN_DEFAULT_SCHEMA_ROOT);
     List<Attribute> attrList = Collections.singletonList(a);
     if (aType.isOperational())
@@ -1008,7 +1002,7 @@
     aType = DirectoryServer.getAttributeType("numsubordinates");
     if (aType == null)
       aType = DirectoryServer.getDefaultAttributeType("numSubordinates");
-    a = Attributes.create("numSubordinates", "0");
+    a = Attributes.create(aType, "0");
     attrList = Collections.singletonList(a);
     if (aType.isOperational())
       operationalAttrs.put(aType, attrList);
@@ -1019,7 +1013,7 @@
     aType = DirectoryServer.getAttributeType("hassubordinates");
     if (aType == null)
       aType = DirectoryServer.getDefaultAttributeType("hasSubordinates");
-    a = Attributes.create("hasSubordinates", "false");
+    a = Attributes.create(aType, "false");
     attrList = Collections.singletonList(a);
     if (aType.isOperational())
       operationalAttrs.put(aType, attrList);
@@ -1030,7 +1024,7 @@
     aType = DirectoryServer.getAttributeType("entrydn");
     if (aType == null)
       aType = DirectoryServer.getDefaultAttributeType("entryDN");
-    a = Attributes.create("entryDN", dnString);
+    a = Attributes.create(aType, dnString);
     attrList = Collections.singletonList(a);
     if (aType.isOperational())
       operationalAttrs.put(aType, attrList);
@@ -1041,8 +1035,8 @@
 
     // ECL Changelog draft change number
     if((aType = DirectoryServer.getAttributeType("changenumber")) == null)
-      aType = DirectoryServer.getDefaultAttributeType("changenumber");
-    a = Attributes.create("changenumber", String.valueOf(draftChangenumber));
+      aType = DirectoryServer.getDefaultAttributeType("changeNumber");
+    a = Attributes.create(aType, String.valueOf(draftChangenumber));
     attrList = new ArrayList<Attribute>(1);
     attrList.add(a);
     if(aType.isOperational())
@@ -1052,7 +1046,7 @@
 
     //
     if((aType = DirectoryServer.getAttributeType("changetime")) == null)
-      aType = DirectoryServer.getDefaultAttributeType("changetime");
+      aType = DirectoryServer.getDefaultAttributeType("changeTime");
     SimpleDateFormat dateFormat;
     dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME);
     dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); // ??
@@ -1075,7 +1069,7 @@
 
     //
     if((aType = DirectoryServer.getAttributeType("changetype")) == null)
-      aType = DirectoryServer.getDefaultAttributeType("changetype");
+      aType = DirectoryServer.getDefaultAttributeType("changeType");
     a = Attributes.create(aType, changetype);
     attrList = new ArrayList<Attribute>(1);
     attrList.add(a);
@@ -1086,7 +1080,7 @@
 
     //
     if((aType = DirectoryServer.getAttributeType("targetdn")) == null)
-      aType = DirectoryServer.getDefaultAttributeType("targetdn");
+      aType = DirectoryServer.getDefaultAttributeType("targetDN");
     a = Attributes.create(aType, targetDN.toNormalizedString());
     attrList = new ArrayList<Attribute>(1);
     attrList.add(a);
@@ -1098,7 +1092,7 @@
     // NON REQUESTED attributes
 
     if((aType = DirectoryServer.getAttributeType("replicationcsn")) == null)
-      aType = DirectoryServer.getDefaultAttributeType("replicationcsn");
+      aType = DirectoryServer.getDefaultAttributeType("replicationCSN");
     a = Attributes.create(aType, changeNumber.toString());
     attrList = new ArrayList<Attribute>(1);
     attrList.add(a);
@@ -1109,7 +1103,7 @@
 
     //
     if((aType = DirectoryServer.getAttributeType("replicaidentifier")) == null)
-      aType = DirectoryServer.getDefaultAttributeType("replicaidentifier");
+      aType = DirectoryServer.getDefaultAttributeType("replicaIdentifier");
     a = Attributes.create(aType, Integer.toString(changeNumber.getServerId()));
     attrList = new ArrayList<Attribute>(1);
     attrList.add(a);
@@ -1146,7 +1140,7 @@
               clearLDIFchanges.substring(start_val_cr+2, end_val_cr);
 
             if((aType =
-              DirectoryServer.getAttributeType("changeInitiatorsName")) == null)
+              DirectoryServer.getAttributeType("changeinitiatorsname")) == null)
               aType =
                 DirectoryServer.getDefaultAttributeType("changeInitiatorsName");
             a = Attributes.create(aType, creatorsName);
@@ -1196,7 +1190,7 @@
               clearLDIFchanges.substring(start_val_cr, end_val_cr);
 
             if((aType =
-              DirectoryServer.getAttributeType("changeInitiatorsName")) == null)
+              DirectoryServer.getAttributeType("changeinitiatorsname")) == null)
               aType =
                 DirectoryServer.getDefaultAttributeType("changeInitiatorsName");
             a = Attributes.create(aType, modifiersName);
@@ -1221,7 +1215,7 @@
 
     if (changetype.equals("delete") && (delInitiatorsName!=null))
     {
-      if((aType = DirectoryServer.getAttributeType("changeInitiatorsName"))
+      if((aType = DirectoryServer.getAttributeType("changeinitiatorsname"))
           == null)
         aType = DirectoryServer.getDefaultAttributeType("changeInitiatorsName");
       a = Attributes.create(aType, delInitiatorsName);
@@ -1236,7 +1230,7 @@
     if (targetUUID != null)
     {
       if((aType = DirectoryServer.getAttributeType("targetentryuuid")) == null)
-        aType = DirectoryServer.getDefaultAttributeType("targetentryuuid");
+        aType = DirectoryServer.getDefaultAttributeType("targetEntryUUID");
       a = Attributes.create(aType, targetUUID);
       attrList = new ArrayList<Attribute>(1);
       attrList.add(a);
@@ -1249,7 +1243,7 @@
       {
         // compat mode
         if((aType = DirectoryServer.getAttributeType("targetuniqueid")) == null)
-          aType = DirectoryServer.getDefaultAttributeType("targetuniqueid");
+          aType = DirectoryServer.getDefaultAttributeType("targetUniqueID");
         String dseeValue = null;
         try
         {
@@ -1283,7 +1277,7 @@
     }
 
     if((aType = DirectoryServer.getAttributeType("changelogcookie")) == null)
-      aType = DirectoryServer.getDefaultAttributeType("changelogcookie");
+      aType = DirectoryServer.getDefaultAttributeType("changeLogCookie");
     a = Attributes.create(aType, cookie);
     attrList = new ArrayList<Attribute>(1);
     attrList.add(a);
@@ -1319,7 +1313,7 @@
     // at the end build the CL entry to be returned
     Entry cle = new Entry(
         DN.decode(dnString),
-        eclObjectClasses,
+        CHANGELOG_ENTRY_OBJECT_CLASSES,
         uAttrs,
         operationalAttrs);
 
@@ -1592,20 +1586,65 @@
    * on attributes that can be optimized in the ECL.
    * When found, populate the provided StartECLSessionMsg.
    * @param startCLmsg the startCLMsg to be populated.
+   * @param baseDN the provided search baseDN.
    * @param sf the provided search filter.
    * @throws DirectoryException when an exception occurs.
    */
-  public static void evaluateFilter(StartECLSessionMsg startCLmsg,
-      SearchFilter sf)
-  throws DirectoryException
+  public static void evaluateSearchParameters(StartECLSessionMsg startCLmsg,
+      DN baseDN, SearchFilter sf) throws DirectoryException
   {
-    StartECLSessionMsg msg = evaluateFilter2(sf);
+    // Select whether to use the DN or the filter.
+    switch (baseDN.getNumComponents())
+    {
+    case 1:
+      // cn=changelog - use user provided search filter.
+      break;
+    case 2:
+      // changeNumber=xxx,cn=changelog - draft ECL - use faked up equality
+      // filter.
+
+      // The DN could also be a new ECL <service-id>,cn=changelog so be sure it
+      // is draft ECL.
+      RDN rdn = baseDN.getRDN();
+
+      AttributeType at = DirectoryServer.getAttributeType("changenumber");
+      if (at == null)
+      {
+        at = DirectoryServer.getDefaultAttributeType("changeNumber");
+      }
+
+      AttributeValue av = rdn.getAttributeValue(at);
+      if (av != null)
+      {
+        sf = SearchFilter.createEqualityFilter(at, av);
+      }
+      break;
+    default:
+      // replicationCSN=xxx,<service-id>,cn=changelog - new ECL - use faked up
+      // equality filter.
+      rdn = baseDN.getRDN();
+
+      at = DirectoryServer.getAttributeType("replicationcsn");
+      if (at == null)
+      {
+        at = DirectoryServer.getDefaultAttributeType("replicationCSN");
+      }
+
+      av = rdn.getAttributeValue(at);
+      if (av != null)
+      {
+        sf = SearchFilter.createEqualityFilter(at, av);
+      }
+      break;
+    }
+
+    StartECLSessionMsg msg = evaluateSearchParameters2(sf);
     startCLmsg.setFirstDraftChangeNumber(msg.getFirstDraftChangeNumber());
     startCLmsg.setLastDraftChangeNumber(msg.getLastDraftChangeNumber());
     startCLmsg.setChangeNumber(msg.getChangeNumber());
   }
 
-  private static StartECLSessionMsg evaluateFilter2(SearchFilter sf)
+  private static StartECLSessionMsg evaluateSearchParameters2(SearchFilter sf)
   throws DirectoryException
   {
     StartECLSessionMsg startCLmsg = new StartECLSessionMsg();
@@ -1665,8 +1704,8 @@
       // Here is the only binary operation we know how to optimize
       Collection<SearchFilter> comps = sf.getFilterComponents();
       SearchFilter sfs[] = comps.toArray(new SearchFilter[0]);
-      StartECLSessionMsg m1 = evaluateFilter2(sfs[0]);
-      StartECLSessionMsg m2 = evaluateFilter2(sfs[1]);
+      StartECLSessionMsg m1 = evaluateSearchParameters2(sfs[0]);
+      StartECLSessionMsg m2 = evaluateSearchParameters2(sfs[1]);
 
       int l1 = m1.getLastDraftChangeNumber();
       int l2 = m2.getLastDraftChangeNumber();
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
index bb3b792..ea2ed87 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ExternalChangeLogTest.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2011 ForgeRock AS
  */
 package org.opends.server.replication;
 
@@ -58,10 +59,6 @@
 import java.util.SortedSet;
 import java.util.TreeSet;
 
-import static org.opends.server.loggers.ErrorLogger.logError;
-import org.opends.messages.Category;
-import org.opends.messages.Message;
-import org.opends.messages.Severity;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.api.Backend;
 import org.opends.server.api.ConnectionHandler;
@@ -179,7 +176,7 @@
   List<Control> NO_CONTROL = null;
 
   private int brokerSessionTimeout = 5000;
-  
+
   private int maxWindow = 100;
   /**
    * Set up the environment for performing the tests in this Class.
@@ -223,12 +220,12 @@
   @Test(enabled=true)
   public void ECLReplicationServerTest()
   {
-    // No RSDomain created yet => RS only case => ECL is not a supported 
+    // No RSDomain created yet => RS only case => ECL is not a supported
     ECLIsNotASupportedSuffix();
 
     // Following test does not create RSDomain (only broker) but want to test
     // ECL .. so let's enable ECl manually
-    // Now that we tested that ECl is not available 
+    // Now that we tested that ECl is not available
     try
     {
       ECLWorkflowElement wfe = (ECLWorkflowElement)
@@ -243,7 +240,7 @@
           +  stackTraceToSingleLineString(de));
     }
 
-    // Test all types of ops.  
+    // Test all types of ops.
     ECLAllOps(); // Do not clean the db for the next test
 
     // First and last should be ok whenever a request has been done or not
@@ -676,10 +673,10 @@
       DN baseDn2 = DN.decode(TEST_ROOT_DN_STRING2);
       SortedSet<String> replServers = new TreeSet<String>();
       replServers.add("localhost:"+replicationServerPort);
-      
+
       DomainFakeCfg domainConf =
         new DomainFakeCfg(baseDn2,  1602, replServers);
-      ExternalChangelogDomainFakeCfg eclCfg = 
+      ExternalChangelogDomainFakeCfg eclCfg =
         new ExternalChangelogDomainFakeCfg(true, null);
       domainConf.setExternalChangelogDomain(eclCfg);
       LDAPReplicationDomain domain2 =
@@ -739,7 +736,7 @@
         }
       }
 
-      eclCfg = 
+      eclCfg =
         new ExternalChangelogDomainFakeCfg(false, null);
       domainConf.setExternalChangelogDomain(eclCfg);
       domain2.applyConfigurationChange(domainConf);
@@ -1127,15 +1124,15 @@
       attributes,
       controls,
       null);
-      
+
       waitOpResult(searchOp, ResultCode.UNWILLING_TO_PERFORM);
       assertEquals(searchOp.getSearchEntries().size(), 0);
       assertTrue(searchOp.getErrorMessage().toString().equals(
           ERR_INVALID_COOKIE_SYNTAX.get().toString()),
           searchOp.getErrorMessage().toString());
-      
+
       // Test unknown domain in provided cookie
-      // This case seems to be very hard to obtain in the real life 
+      // This case seems to be very hard to obtain in the real life
       // (how to remove a domain from a RS topology ?)
       // let's do a very quick test here.
       String newCookie = lastCookie + "o=test6:";
@@ -1157,7 +1154,7 @@
       attributes,
       controls,
       null);
-      
+
       waitOpResult(searchOp, ResultCode.UNWILLING_TO_PERFORM);
       assertEquals(searchOp.getSearchEntries().size(), 0);
       assertTrue(searchOp.getErrorMessage().toString().startsWith(
@@ -1184,7 +1181,7 @@
       attributes,
       controls,
       null);
-      
+
       waitOpResult(searchOp, ResultCode.UNWILLING_TO_PERFORM);
       assertEquals(searchOp.getSearchEntries().size(), 0);
       String expectedError = ERR_RESYNC_REQUIRED_MISSING_DOMAIN_IN_PROVIDED_COOKIE
@@ -1254,7 +1251,7 @@
 
       // Sleep longer than this delay - the changelog will be trimmed
       Thread.sleep(1000);
-      
+
       // ---
       // 2. Now set up a very short purge delay on the replication changelogs
       // so that this test can play with a trimmed changelog.
@@ -1336,9 +1333,9 @@
 
       // Assert ECL is empty since replication changelog has been trimmed
       assertEquals(searchOp.getSearchEntries().size(), 0);
-      
+
       // ---
-      // 5. Assert that a request with an "old" cookie - one that refers to 
+      // 5. Assert that a request with an "old" cookie - one that refers to
       //    changes that have been removed by the replication changelog trimming
       //    returns the appropriate error.
 
@@ -1381,7 +1378,7 @@
       // And reset changelog purge delay for the other tests.
       d1.setPurgeDelay(15 * 1000);
       d2.setPurgeDelay(15 * 1000);
-      
+
     }
     catch(Exception e)
     {
@@ -3262,7 +3259,7 @@
             NO_CONTROL,
             null);
       waitOpResult(searchOp, ResultCode.SUCCESS);
-      assertEquals(searchOp.getSearchEntries().size(), 
+      assertEquals(searchOp.getSearchEntries().size(),
           lastDraftChangeNumber-firstDraftChangeNumber+1);
       if (searchOp.getSearchEntries() != null)
       {
@@ -3394,29 +3391,31 @@
     {
       StartECLSessionMsg startCLmsg = new StartECLSessionMsg();
 
+      DN baseDN = DN.decode("cn=changelog");
+
       //
-      ECLSearchOperation.evaluateFilter(startCLmsg,
-          SearchFilter.createFilterFromString("(objectclass=*)"));
+      ECLSearchOperation.evaluateSearchParameters(startCLmsg,
+          baseDN, SearchFilter.createFilterFromString("(objectclass=*)"));
       assertEquals(startCLmsg.getFirstDraftChangeNumber(),-1);
       assertEquals(startCLmsg.getLastDraftChangeNumber(),-1);
 
       //
-      ECLSearchOperation.evaluateFilter(startCLmsg,
-          SearchFilter.createFilterFromString("(changenumber>=2)"));
+      ECLSearchOperation.evaluateSearchParameters(startCLmsg,
+          baseDN, SearchFilter.createFilterFromString("(changenumber>=2)"));
       assertEquals(startCLmsg.getFirstDraftChangeNumber(),2);
       assertEquals(startCLmsg.getLastDraftChangeNumber(),-1);
 
       //
-      ECLSearchOperation.evaluateFilter(startCLmsg,
-          SearchFilter.createFilterFromString("(&(changenumber>=2)(changenumber<=5))"));
+      ECLSearchOperation.evaluateSearchParameters(startCLmsg,
+          baseDN, SearchFilter.createFilterFromString("(&(changenumber>=2)(changenumber<=5))"));
       assertEquals(startCLmsg.getFirstDraftChangeNumber(),2);
       assertEquals(startCLmsg.getLastDraftChangeNumber(),5);
 
       //
       try
       {
-        ECLSearchOperation.evaluateFilter(startCLmsg,
-            SearchFilter.createFilterFromString("(&(changenumber>=2)(changenumber<+5))"));
+        ECLSearchOperation.evaluateSearchParameters(startCLmsg,
+            baseDN, SearchFilter.createFilterFromString("(&(changenumber>=2)(changenumber<+5))"));
         assertTrue((startCLmsg.getFirstDraftChangeNumber()==1));
       }
       catch(DirectoryException de)
@@ -3425,38 +3424,51 @@
       }
 
       //
-      ECLSearchOperation.evaluateFilter(startCLmsg,
-          SearchFilter.createFilterFromString("(&(dc=x)(&(changenumber>=2)(changenumber<=5)))"));
+      ECLSearchOperation.evaluateSearchParameters(startCLmsg,
+          baseDN, SearchFilter.createFilterFromString("(&(dc=x)(&(changenumber>=2)(changenumber<=5)))"));
       assertEquals(startCLmsg.getFirstDraftChangeNumber(),2);
       assertEquals(startCLmsg.getLastDraftChangeNumber(),5);
 
-      ECLSearchOperation.evaluateFilter(startCLmsg,
-          SearchFilter.createFilterFromString("(&(&(changenumber>=3)(changenumber<=4))(&(|(dc=y)(dc=x))(&(changenumber>=2)(changenumber<=5))))"));
+      ECLSearchOperation.evaluateSearchParameters(startCLmsg,
+          baseDN, SearchFilter.createFilterFromString("(&(&(changenumber>=3)(changenumber<=4))(&(|(dc=y)(dc=x))(&(changenumber>=2)(changenumber<=5))))"));
       assertEquals(startCLmsg.getFirstDraftChangeNumber(),3);
       assertEquals(startCLmsg.getLastDraftChangeNumber(),4);
 
       //
-      ECLSearchOperation.evaluateFilter(startCLmsg,
-          SearchFilter.createFilterFromString("(|(objectclass=*)(&(changenumber>=2)(changenumber<=5)))"));
+      ECLSearchOperation.evaluateSearchParameters(startCLmsg,
+          baseDN, SearchFilter.createFilterFromString("(|(objectclass=*)(&(changenumber>=2)(changenumber<=5)))"));
       assertEquals(startCLmsg.getFirstDraftChangeNumber(),-1);
       assertEquals(startCLmsg.getLastDraftChangeNumber(),-1);
 
       //
-      ECLSearchOperation.evaluateFilter(startCLmsg,
-          SearchFilter.createFilterFromString("(changenumber=8)"));
+      ECLSearchOperation.evaluateSearchParameters(startCLmsg,
+          baseDN, SearchFilter.createFilterFromString("(changenumber=8)"));
       assertEquals(startCLmsg.getFirstDraftChangeNumber(),8);
       assertEquals(startCLmsg.getLastDraftChangeNumber(),8);
 
       //
       ChangeNumberGenerator gen = new ChangeNumberGenerator( 1, 0);
       ChangeNumber changeNumber1 = gen.newChangeNumber();
-      ECLSearchOperation.evaluateFilter(startCLmsg,
-          SearchFilter.createFilterFromString("(replicationcsn="+changeNumber1+")"));
+      ECLSearchOperation.evaluateSearchParameters(startCLmsg,
+          baseDN, SearchFilter.createFilterFromString("(replicationcsn="+changeNumber1+")"));
       assertEquals(startCLmsg.getFirstDraftChangeNumber(),-1);
       assertEquals(startCLmsg.getLastDraftChangeNumber(),-1);
       assertEquals(startCLmsg.getChangeNumber(), changeNumber1);
 
+      // Use change number as base object.
+      baseDN = DN.decode("changeNumber=8,cn=changelog");
 
+      //
+      ECLSearchOperation.evaluateSearchParameters(startCLmsg,
+          baseDN, SearchFilter.createFilterFromString("(objectclass=*)"));
+      assertEquals(startCLmsg.getFirstDraftChangeNumber(),8);
+      assertEquals(startCLmsg.getLastDraftChangeNumber(),8);
+
+      // The base DN should take preference.
+      ECLSearchOperation.evaluateSearchParameters(startCLmsg,
+          baseDN, SearchFilter.createFilterFromString("(changenumber>=2)"));
+      assertEquals(startCLmsg.getFirstDraftChangeNumber(),8);
+      assertEquals(startCLmsg.getLastDraftChangeNumber(),8);
     }
     catch(Exception e)
     {
@@ -3563,7 +3575,7 @@
                 null);
             assertEquals(getAttributeValue(resultEntry, "lastExternalChangelogCookie"),
                 null);
-            
+
           }
         }
       }
@@ -3673,7 +3685,7 @@
 
       ServerState ss = new ServerState();
       ss.update(cn1);
-      
+
       // From state/cn1(exclusive) to cn1 (inclusive) : 0 change
       count = rsdtest.getEligibleCount(ss, cn1);
       assertEquals(count, 0);
@@ -3708,7 +3720,7 @@
       boolean perfs=false;
       if (perfs)
       {
-      
+
       // number of msgs used by the test
       int maxMsg = 999999;
 
@@ -3722,7 +3734,7 @@
         delMsg =
           new DeleteMsg("uid="+tn+i+"," + TEST_ROOT_DN_STRING, cnx,
               user1entryUUID);
-        server01.publish(delMsg);  
+        server01.publish(delMsg);
       }
       sleep(1000);
       debugInfo(tn, "Perfs test in compat - search lastChangeNumber");
@@ -3742,7 +3754,7 @@
       assertEquals(limitss[1], maxMsg);
       long t2 = TimeThread.getTime();
       debugInfo(tn, "Perfs - " + maxMsg + " counted in (ms):" + (t2 - t1));
-      
+
       try
       {
         // search on 'cn=changelog'
@@ -3866,7 +3878,7 @@
       SortedSet<AttributeType> eclInclude = new TreeSet<AttributeType>();
       eclInclude.add(DirectoryServer.getAttributeType("sn"));
       eclInclude.add(DirectoryServer.getAttributeType("roomnumber"));
-      ExternalChangelogDomainFakeCfg eclCfg = 
+      ExternalChangelogDomainFakeCfg eclCfg =
         new ExternalChangelogDomainFakeCfg(true, eclInclude);
       domainConf.setExternalChangelogDomain(eclCfg);
       // Set a Changetime heartbeat interval low enough (less than default
@@ -3885,7 +3897,7 @@
       // on o=test3,sid=1703 include attrs set to : 'objectclass'
       eclInclude = new TreeSet<AttributeType>();
       eclInclude.add(DirectoryServer.getAttributeType("objectclass"));
-      eclCfg = 
+      eclCfg =
         new ExternalChangelogDomainFakeCfg(true, eclInclude);
       domainConf.setExternalChangelogDomain(eclCfg);
       // Set a Changetime heartbeat interval low enough (less than default
@@ -3900,7 +3912,7 @@
         new DomainFakeCfg(baseDn2, 1704, replServers);
       eclInclude = new TreeSet<AttributeType>();
       eclInclude.add(DirectoryServer.getAttributeType("cn"));
-      eclCfg = 
+      eclCfg =
         new ExternalChangelogDomainFakeCfg(true, eclInclude);
       domainConf.setExternalChangelogDomain(eclCfg);
       // Set a Changetime heartbeat interval low enough (less than default
@@ -3957,7 +3969,7 @@
       modOpBasis.run();
       waitOpResult(modOpBasis, ResultCode.SUCCESS);
 
-      // mod 'telephonenumber' of robert (o=test3) 
+      // mod 'telephonenumber' of robert (o=test3)
       builder = new AttributeBuilder("telephonenumber");
       builder.add("555555");
       mod =
@@ -4086,14 +4098,14 @@
 
         if (domain3 != null)
           MultimasterReplication.deleteDomain(baseDn3);
-        removeTestBackend2(backend3);    
+        removeTestBackend2(backend3);
 
       }
       catch(Exception e) {}
     }
     debugInfo(tn, "Ending test with success");
   }
-  
+
   private void waitOpResult(AbstractOperation operation,
       ResultCode expectedResult)
   {
@@ -4104,8 +4116,8 @@
       sleep(50);
       ii++;
       if (ii>10)
-        assertEquals(operation.getResultCode(), expectedResult, 
-            operation.getErrorMessage().toString());                
+        assertEquals(operation.getResultCode(), expectedResult,
+            operation.getErrorMessage().toString());
     }
   }
 }

--
Gitblit v1.10.0