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

pgamba
14.37.2009 4fe72a4bef946169b0f50bc05bd9dc3b4b1131d3
opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
@@ -37,12 +37,12 @@
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
@@ -81,7 +81,6 @@
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.replication.service.ReplicationDomain;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
@@ -94,6 +93,7 @@
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;
@@ -107,7 +107,6 @@
import org.opends.server.types.operation.SearchReferenceSearchOperation;
import org.opends.server.util.Base64;
import org.opends.server.util.ServerConstants;
import org.opends.server.util.TimeThread;
@@ -136,22 +135,12 @@
  // The associated DN.
  private DN rootBaseDN;
  // The cookie received in the ECL request control coming along
  // with the request.
  MultiDomainServerState requestCookie = null;
  /**
   * The replication server in which the search on ECL is to be performed.
   */
  protected ReplicationServer replicationServer;
  /**
   * Indicates whether we should actually process the search.  This should
   * only be false if it's a persistent search with changesOnly=true.
   */
  protected boolean changesOnly;
  /**
   * The client connection for the search operation.
   */
  protected ClientConnection clientConnection;
@@ -177,6 +166,7 @@
  private HashSet<String> supportedControls;
  // The set of supported features for this WE
  // TODO: any special feature to be implemented for an ECL search operation ?
  private HashSet<String> supportedFeatures;
  String privateDomainsBaseDN;
@@ -202,11 +192,12 @@
    ObjectClass topOC = DirectoryServer.getObjectClass(OC_TOP, true);
    eclObjectClasses.put(topOC, OC_TOP);
    ObjectClass eclEntryOC = DirectoryServer.getObjectClass(OC_CHANGELOG_ENTRY,
                                                           true);
        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);
@@ -226,57 +217,79 @@
   *           if this operation should be cancelled
   */
  public void processECLSearch(ECLWorkflowElement wfe)
      throws CanceledOperationException
  throws CanceledOperationException
  {
    boolean executePostOpPlugins = false;
    this.replicationServer = wfe.getReplicationServer();
    clientConnection = getClientConnection();
    // Get the plugin config manager that will be used for invoking plugins.
    PluginConfigManager pluginConfigManager =
      DirectoryServer.getPluginConfigManager();
    changesOnly = false;
    // Check for a request to cancel this operation.
    checkIfCanceled(false);
    // Create a labeled block of code that we can break out of if a problem is
    // detected.
searchProcessing:
    searchProcessing:
    {
      replicationServer  = wfe.getReplicationServer();
      clientConnection   = getClientConnection();
      startECLSessionMsg = new StartECLSessionMsg();
      // Set default behavior as "from draft change number".
      // "from cookie" is set only when cookie is provided.
      startECLSessionMsg.setECLRequestType(
          StartECLSessionMsg.REQUEST_TYPE_FROM_DRAFT_CHANGE_NUMBER);
      // Set a string operationid that will help correlate any error message
      // logged for this operation with the 'real' client operation.
      startECLSessionMsg.setOperationId(this.toString());
      // Set a list of excluded domains (also exclude 'cn=changelog' itself)
      ArrayList<String> excludedDomains =
        MultimasterReplication.getPrivateDomains();
      if (!excludedDomains.contains(ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT))
        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.
      baseDN = getBaseDN();
      filter = getFilter();
      if ((baseDN == null) || (filter == null)){
        break searchProcessing;
      }
      // Check to see if there are any controls in the request.  If so, then
      // see if there is any special processing required.
      // Analyse controls - including the cookie control
      try
      {
        this.requestCookie = null;
        handleRequestControls();
        if (this.requestCookie == null)
        {
          setResponseData(new DirectoryException(
              ResultCode.OPERATIONS_ERROR,
              Message.raw(Category.SYNC, Severity.FATAL_ERROR,
                  "Cookie control expected")));
          break searchProcessing;
        }
      }
      catch (DirectoryException de)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, de);
        }
        setResponseData(de);
        break searchProcessing;
      }
      // Process filter - extract draft change number (seqnum) conditions
      try
      {
        evaluateFilter(startECLSessionMsg, this.getFilter());
      }
      catch (DirectoryException de)
      {
        if (debugEnabled())
          TRACER.debugCaught(DebugLogLevel.ERROR, de);
        setResponseData(de);
        break searchProcessing;
      }
@@ -287,7 +300,7 @@
      // Invoke the pre-operation search plugins.
      executePostOpPlugins = true;
      PluginResult.PreOperation preOpResult =
          pluginConfigManager.invokePreOperationSearchPlugins(this);
        pluginConfigManager.invokePreOperationSearchPlugins(this);
      if (!preOpResult.continueProcessing())
      {
        setResultCode(preOpResult.getResultCode());
@@ -297,26 +310,12 @@
        break searchProcessing;
      }
      // Check for a request to cancel this operation.
      checkIfCanceled(false);
      // Test existence of the RS
      if (replicationServer == null)
      {
        setResultCode(ResultCode.OPERATIONS_ERROR);
        appendErrorMessage(ERR_SEARCH_BASE_DOESNT_EXIST.get(
                                String.valueOf(baseDN)));
        break searchProcessing;
      }
      // We'll set the result code to "success".  If a problem occurs, then it
      // will be overwritten.
      // Be optimistic by default.
      setResultCode(ResultCode.SUCCESS);
      // If there's a persistent search, then register it with the server.
      if (persistentSearch != null)
      {
@@ -332,9 +331,7 @@
      catch (DirectoryException de)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, de);
        }
        setResponseData(de);
@@ -343,7 +340,6 @@
          persistentSearch.cancel();
          setSendResponse(true);
        }
        break searchProcessing;
      }
      catch (CanceledOperationException coe)
@@ -353,31 +349,25 @@
          persistentSearch.cancel();
          setSendResponse(true);
        }
        throw coe;
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        setResultCode(DirectoryServer.getServerErrorResultCode());
        appendErrorMessage(ERR_SEARCH_BACKEND_EXCEPTION.get(
                                getExceptionMessage(e)));
            getExceptionMessage(e)));
        if (persistentSearch != null)
        {
          persistentSearch.cancel();
          setSendResponse(true);
        }
        break searchProcessing;
      }
    }
    // Check for a request to cancel this operation.
    checkIfCanceled(false);
@@ -385,7 +375,7 @@
    if (executePostOpPlugins)
    {
      PluginResult.PostOperation postOpResult =
           pluginConfigManager.invokePostOperationSearchPlugins(this);
        pluginConfigManager.invokePostOperationSearchPlugins(this);
      if (!postOpResult.continueProcessing())
      {
        setResultCode(postOpResult.getResultCode());
@@ -398,13 +388,13 @@
  /**
   * Handles any controls contained in the request.
   * Handles any controls contained in the request - including the cookie ctrl.
   *
   * @throws  DirectoryException  If there is a problem with any of the request
   *                              controls.
   */
  protected void handleRequestControls()
          throws DirectoryException
  throws DirectoryException
  {
    List<Control> requestControls  = getRequestControls();
    if ((requestControls != null) && (! requestControls.isEmpty()))
@@ -414,22 +404,28 @@
        Control c   = requestControls.get(i);
        String  oid = c.getOID();
        if (! AccessControlConfigManager.getInstance().
                   getAccessControlHandler().isAllowed(baseDN, this, c))
            getAccessControlHandler().isAllowed(baseDN, this, c))
        {
          throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
                         ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
              ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
        }
        if (oid.equals(OID_ECL_COOKIE_EXCHANGE_CONTROL))
        {
          ExternalChangelogRequestControl eclControl =
            getRequestControl(ExternalChangelogRequestControl.DECODER);
          this.requestCookie = eclControl.getCookie();
          MultiDomainServerState cookie = eclControl.getCookie();
          if (cookie!=null)
          {
            startECLSessionMsg.setECLRequestType(
                StartECLSessionMsg.REQUEST_TYPE_FROM_COOKIE);
            startECLSessionMsg.setCrossDomainServerState(cookie.toString());
          }
        }
        else if (oid.equals(OID_LDAP_ASSERTION))
        {
          LDAPAssertionRequestControl assertControl =
                getRequestControl(LDAPAssertionRequestControl.DECODER);
            getRequestControl(LDAPAssertionRequestControl.DECODER);
          try
          {
@@ -449,20 +445,20 @@
              }
              throw new DirectoryException(de.getResultCode(),
                             ERR_SEARCH_CANNOT_GET_ENTRY_FOR_ASSERTION.get(
                                  de.getMessageObject()));
                  ERR_SEARCH_CANNOT_GET_ENTRY_FOR_ASSERTION.get(
                      de.getMessageObject()));
            }
            if (entry == null)
            {
              throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
                             ERR_SEARCH_NO_SUCH_ENTRY_FOR_ASSERTION.get());
                  ERR_SEARCH_NO_SUCH_ENTRY_FOR_ASSERTION.get());
            }
            if (! assertionFilter.matchesEntry(entry))
            {
              throw new DirectoryException(ResultCode.ASSERTION_FAILED,
                                           ERR_SEARCH_ASSERTION_FAILED.get());
                  ERR_SEARCH_ASSERTION_FAILED.get());
            }
          }
          catch (DirectoryException de)
@@ -478,8 +474,8 @@
            }
            throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                           ERR_SEARCH_CANNOT_PROCESS_ASSERTION_FILTER.get(
                                de.getMessageObject()), de);
                ERR_SEARCH_CANNOT_PROCESS_ASSERTION_FILTER.get(
                    de.getMessageObject()), de);
          }
        }
        else if (oid.equals(OID_PROXIED_AUTH_V1))
@@ -489,11 +485,11 @@
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
                ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV1Control proxyControl =
              getRequestControl(ProxiedAuthV1Control.DECODER);
            getRequestControl(ProxiedAuthV1Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
@@ -513,11 +509,11 @@
          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
          {
            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
                ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }
          ProxiedAuthV2Control proxyControl =
              getRequestControl(ProxiedAuthV2Control.DECODER);
            getRequestControl(ProxiedAuthV2Control.DECODER);
          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
@@ -536,15 +532,17 @@
            getRequestControl(PersistentSearchControl.DECODER);
          persistentSearch = new PersistentSearch(this,
                                      psearchControl.getChangeTypes(),
                                      psearchControl.getReturnECs());
              psearchControl.getChangeTypes(),
              psearchControl.getReturnECs());
          // If we're only interested in changes, then we don't actually want
          // to process the search now.
          if (psearchControl.getChangesOnly())
          {
            changesOnly = true;
          }
          if (!psearchControl.getChangesOnly())
            startECLSessionMsg.setPersistent(
                StartECLSessionMsg.PERSISTENT);
          else
            startECLSessionMsg.setPersistent(
                StartECLSessionMsg.PERSISTENT_CHANGES_ONLY);
        }
        else if (oid.equals(OID_LDAP_SUBENTRIES))
        {
@@ -553,7 +551,7 @@
        else if (oid.equals(OID_MATCHED_VALUES))
        {
          MatchedValuesControl matchedValuesControl =
                getRequestControl(MatchedValuesControl.DECODER);
            getRequestControl(MatchedValuesControl.DECODER);
          setMatchedValuesControl(matchedValuesControl);
        }
        else if (oid.equals(OID_ACCOUNT_USABLE_CONTROL))
@@ -569,20 +567,19 @@
          setVirtualAttributesOnly(true);
        }
        else if (oid.equals(OID_GET_EFFECTIVE_RIGHTS) &&
          DirectoryServer.isSupportedControl(OID_GET_EFFECTIVE_RIGHTS))
            DirectoryServer.isSupportedControl(OID_GET_EFFECTIVE_RIGHTS))
        {
          // Do nothing here and let AciHandler deal with it.
        }
        // NYI -- Add support for additional controls.
        // TODO: Add support for additional controls, including VLV
        else if (c.isCritical())
        {
          if ((replicationServer == null) || (! supportsControl(oid)))
          {
            throw new DirectoryException(
                           ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
                           ERR_SEARCH_UNSUPPORTED_CRITICAL_CONTROL.get(oid));
                ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
                ERR_SEARCH_UNSUPPORTED_CRITICAL_CONTROL.get(oid));
          }
        }
      }
@@ -592,35 +589,12 @@
  private void processSearch()
  throws DirectoryException, CanceledOperationException
  {
    startECLSessionMsg = new StartECLSessionMsg();
    startECLSessionMsg.setECLRequestType(
        StartECLSessionMsg.REQUEST_TYPE_FROM_COOKIE);
    startECLSessionMsg.setChangeNumber(
        new ChangeNumber(TimeThread.getTime(),(short)0, (short)0));
    startECLSessionMsg.setCrossDomainServerState(requestCookie.toString());
    if (debugEnabled())
      TRACER.debugInfo(
        " processSearch toString=[" + toString() + "] opid=["
        + startECLSessionMsg.getOperationId() + "]");
    if (persistentSearch==null)
      startECLSessionMsg.setPersistent(StartECLSessionMsg.NON_PERSISTENT);
    else
      if (!changesOnly)
        startECLSessionMsg.setPersistent(StartECLSessionMsg.PERSISTENT);
      else
        startECLSessionMsg.setPersistent(
            StartECLSessionMsg.PERSISTENT_CHANGES_ONLY);
    startECLSessionMsg.setFirstDraftChangeNumber(0);
    startECLSessionMsg.setLastDraftChangeNumber(0);
    // Help correlate with access log with the format: "conn=x op=y msgID=z"
    startECLSessionMsg.setOperationId(
      "conn="+String.valueOf(this.getConnectionID())
      + " op="+String.valueOf(this.getOperationID())
      + " msgID="+String.valueOf(getOperationID()));
    startECLSessionMsg.setExcludedDNs(
        MultimasterReplication.getPrivateDomains());
    // Start session
    // Start a specific ECL session
    eclSession = replicationServer.createECLSession(startECLSessionMsg);
    if (!getScope().equals(SearchScope.SINGLE_LEVEL))
@@ -718,9 +692,9 @@
  private boolean matchFilter(Entry entry)
  throws DirectoryException
  {
    boolean ms = entry.matchesBaseAndScope(getBaseDN(), getScope());
    boolean mf = getFilter().matchesEntry(entry);
    return (ms && mf);
    boolean baseScopeMatch = entry.matchesBaseAndScope(getBaseDN(), getScope());
    boolean filterMatch = getFilter().matchesEntry(entry);
    return (baseScopeMatch && filterMatch);
  }
  /**
@@ -755,95 +729,99 @@
          null, // real time current entry
          null, // real time attrs names
          null, // hist entry attributes
          -1,    // TODO:ECL G Good changelog draft compat. addMsg.getSeqnum()
          eclmsg.getDraftChangeNumber(),
      "add");
    } else
    if (msg instanceof ModifyMsg)
    {
      ModifyMsg modMsg = (ModifyMsg)msg;
      InternalClientConnection conn =
        InternalClientConnection.getRootConnection();
      try
      if (msg instanceof ModifyMsg)
      {
        // Map the modMsg modifications to an LDIF string
        // for the 'changes' attribute of the CL entry
        ModifyOperation modifyOperation =
          (ModifyOperation)modMsg.createOperation(conn);
        String LDIFchanges = modToLDIF(modifyOperation.getModifications());
        ModifyMsg modMsg = (ModifyMsg)msg;
        InternalClientConnection conn =
          InternalClientConnection.getRootConnection();
        try
        {
          // Map the modMsg modifications to an LDIF string
          // for the 'changes' attribute of the CL entry
          ModifyOperation modifyOperation =
            (ModifyOperation)modMsg.createOperation(conn);
          String LDIFchanges = modToLDIF(modifyOperation.getModifications());
        // TODO:ECL G Good changelog draft compat. Hist entry attributes
        // ArrayList<RawAttribute> attributes = modMsg.getEntryAttributes();
          // TODO:ECL Hist entry attributes
          // ArrayList<RawAttribute> attributes = modMsg.getEntryAttributes();
          clEntry = createChangelogEntry(
              eclmsg.getServiceId(),
              eclmsg.getCookie().toString(),
              DN.decode(modMsg.getDn()),
              modMsg.getChangeNumber(),
              LDIFchanges,
              modMsg.getUniqueId(),
              null, // real time current entry
              null, // real time attrs names
              null, // hist entry attributes
              eclmsg.getDraftChangeNumber(),
          "modify");
        }
        catch(Exception e)
        {
          // Exceptions raised by createOperation for example
          throw new DirectoryException(ResultCode.OTHER,
              Message.raw(Category.SYNC, Severity.NOTICE,
                  " Server fails to create entry: "),e);
        }
      }
      else if (msg instanceof ModifyDNMsg)
      {
        ModifyDNMsg modDNMsg = (ModifyDNMsg)msg;
        clEntry = createChangelogEntry(
            eclmsg.getServiceId(),
            eclmsg.getCookie().toString(),
            DN.decode(modMsg.getDn()),
            modMsg.getChangeNumber(),
            LDIFchanges,
            modMsg.getUniqueId(),
            DN.decode(modDNMsg.getDn()),
            modDNMsg.getChangeNumber(),
            null,
            modDNMsg.getUniqueId(),
            null, // real time current entry
            null, // real time attrs names
            null, // hist entry attributes
            -1,    // TODO:ECL G Good changelog draft compat. modMsg.getSeqnum()
        "modify");
            eclmsg.getDraftChangeNumber(),
        "modrdn");
        Attribute a = Attributes.create("newrdn", modDNMsg.getNewRDN());
        clEntry.addAttribute(a, null);
        Attribute b = Attributes.create("newsuperior",
            modDNMsg.getNewSuperior());
        clEntry.addAttribute(b, null);
        Attribute c = Attributes.create("deleteoldrdn",
            String.valueOf(modDNMsg.deleteOldRdn()));
        clEntry.addAttribute(c, null);
      }
      catch(Exception e)
      else if (msg instanceof DeleteMsg)
      {
        // FIXME:ECL Handle error when createOperation raise DataFormatExceptin
        DeleteMsg delMsg = (DeleteMsg)msg;
        /* TODO:ECL Entry attributes for DEL op
        ArrayList<RawAttribute> rattributes = new ArrayList<RawAttribute>();
        ArrayList<RawAttribute> rattributes = delMsg.getEntryAttributes();
        // Map the entry attributes of the DelMsg to an LDIF string
        // for the 'deletedentryattributes' attribute of the CL entry
        String delAttrs = delMsgToLDIFString(rattributes);
        */
        clEntry = createChangelogEntry(
            eclmsg.getServiceId(),
            eclmsg.getCookie().toString(),
            DN.decode(delMsg.getDn()),
            delMsg.getChangeNumber(),
            null,
            delMsg.getUniqueId(),
            null,
            null,
            null, //rattributes,
            eclmsg.getDraftChangeNumber(),
        "delete");
      }
    }
    else if (msg instanceof ModifyDNMsg)
    {
      ModifyDNMsg modDNMsg = (ModifyDNMsg)msg;
      clEntry = createChangelogEntry(
          eclmsg.getServiceId(),
          eclmsg.getCookie().toString(),
          DN.decode(modDNMsg.getDn()),
          modDNMsg.getChangeNumber(),
          null,
          modDNMsg.getUniqueId(),
          null, // real time current entry
          null, // real time attrs names
          null, // hist entry attributes
          -1,    // TODO:ECL G Good changelog draft compat. modDNMsg.getSeqnum()
          "modrdn");
      Attribute a = Attributes.create("newrdn", modDNMsg.getNewRDN());
      clEntry.addAttribute(a, null);
      Attribute b = Attributes.create("newsuperior", modDNMsg.getNewSuperior());
      clEntry.addAttribute(b, null);
      Attribute c = Attributes.create("deleteoldrdn",
          String.valueOf(modDNMsg.deleteOldRdn()));
      clEntry.addAttribute(c, null);
    }
    else if (msg instanceof DeleteMsg)
    {
      DeleteMsg delMsg = (DeleteMsg)msg;
      ArrayList<RawAttribute> rattributes = new ArrayList<RawAttribute>();
      /* TODO:ECL Entry attributes for DEL op
      ArrayList<RawAttribute> rattributes = delMsg.getEntryAttributes();
      // Map the entry attributes of the DelMsg to an LDIF string
      // for the 'deletedentryattributes' attribute of the CL entry
      String delAttrs = delMsgToLDIFString(rattributes);
      */
      clEntry = createChangelogEntry(
          eclmsg.getServiceId(),
          eclmsg.getCookie().toString(),
          DN.decode(delMsg.getDn()),
          delMsg.getChangeNumber(),
          null,
          delMsg.getUniqueId(),
          null,
          null,
          null, //rattributes,
          -1, // TODO:ECL G Good changelog draft compat. delMsg.getSeqnum()
         "delete");
    }
    return clEntry;
  }
@@ -863,20 +841,6 @@
    HashMap<AttributeType,List<Attribute>> operationalAttrs =
      new LinkedHashMap<AttributeType,List<Attribute>>();
    // Add to the root entry the replication state of each domain
    // TODO:ECL Put in ECL root entry, the ServerState for each domain
    AttributeType descType = DirectoryServer.getAttributeType("description");
    List<ReplicationDomain> supportedReplicationDomains
     = new ArrayList<ReplicationDomain>();
    for (ReplicationDomain domain : supportedReplicationDomains)
    {
      //    Crappy stuff to return the server state to the client
      LinkedList<Attribute> attrList = new LinkedList<Attribute>();
      attrList.add(Attributes.create("description",
          domain.getServiceID() + "/" + domain.getServerState().toString()));
      userAttrs.put(descType, attrList);
    }
    Entry e = new Entry(this.rootBaseDN, oclasses, userAttrs,
        operationalAttrs);
    return e;
@@ -916,10 +880,19 @@
      String changetype)
  throws DirectoryException
  {
    String dnString = "cn="+ changeNumber +"," +
      serviceID + "," +
    String dnString = "";
    if (draftChangenumber == 0)
    {
      // Draft uncompat mode
      dnString = "cn="+ changeNumber +"," + serviceID + "," +
        ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT;
    }
    else
    {
      // Draft compat mode
      dnString = "cn="+ draftChangenumber + "," +
      ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT;
    }
    HashMap<ObjectClass,String> oClasses =
      new LinkedHashMap<ObjectClass,String>(3);
    oClasses.putAll(eclObjectClasses);
@@ -962,7 +935,7 @@
    attrList = new ArrayList<Attribute>(1);
    attrList.add(a);
    uAttrs.put(a.getAttributeType(), attrList);
    */
     */
    //
    a = Attributes.create("changetype", changetype);
@@ -1017,18 +990,21 @@
      uAttrs.put(a.getAttributeType(), attrList);
    }
    a = Attributes.create("targetentryuuid", targetUUID);
    attrList = new ArrayList<Attribute>(1);
    attrList.add(a);
    operationalAttrs.put(a.getAttributeType(), attrList);
    if (draftChangenumber>0)
    if (targetUUID != null)
    {
      // compat mode
      a = Attributes.create("targetuniqueid",
          ECLSearchOperation.openDsToSunDseeNsUniqueId(targetUUID));
      a = Attributes.create("targetentryuuid", targetUUID);
      attrList = new ArrayList<Attribute>(1);
      attrList.add(a);
      operationalAttrs.put(a.getAttributeType(), attrList);
      if (draftChangenumber>0)
      {
        // compat mode
        a = Attributes.create("targetuniqueid",
            ECLSearchOperation.openDsToSunDseeNsUniqueId(targetUUID));
        attrList = new ArrayList<Attribute>(1);
        attrList.add(a);
        operationalAttrs.put(a.getAttributeType(), attrList);
      }
    }
    a = Attributes.create("changelogcookie", cookie);
@@ -1061,7 +1037,7 @@
        uAttrs.put(a.getAttributeType(), attrList);
      }
    }
    */
     */
    /*
    if (targetAttrNames != null)
@@ -1082,7 +1058,7 @@
        }
      }
    }
    */
     */
    /* TODO: Implement entry attributes historical values
    if (histEntryAttributes != null)
    {
@@ -1105,7 +1081,7 @@
        }
      }
    }
    */
     */
    // at the end build the CL entry to be returned
    Entry cle = new Entry(
@@ -1315,7 +1291,7 @@
   */
  public CancelResult cancel(CancelRequest cancelRequest)
  {
    if (eclSession!=null)
    if (eclSession != null)
    {
      try
      {
@@ -1327,20 +1303,16 @@
  }
  /**
  * The unique identifier used in DSEE is named nsUniqueId and its format is
  * HHHHHHHH-HHHHHHHH-HHHHHHHH-HHHHHHHH where H is a hex digit.
  * An nsUniqueId value is for example 3970de28-08b311d9-8095b9bf-c4d9231c
  * The unique identifier used in OpenDS is named entryUUID.
  * Its value is for example entryUUID: 50dd9673-71e1-4478-b13c-dba387c4d7e1
  * @param entryUid the OpenDS entry UID
  * @return the Dsee format for the entry UID
  */
   * The unique identifier used in DSEE is named nsUniqueId and its format is
   * HHHHHHHH-HHHHHHHH-HHHHHHHH-HHHHHHHH where H is a hex digit.
   * An nsUniqueId value is for example 3970de28-08b311d9-8095b9bf-c4d9231c
   * The unique identifier used in OpenDS is named entryUUID.
   * Its value is for example entryUUID: 50dd9673-71e1-4478-b13c-dba387c4d7e1
   * @param entryUid the OpenDS entry UID
   * @return the Dsee format for the entry UID
   */
  private static String openDsToSunDseeNsUniqueId(String entryUid)
  {
    if (entryUid == null)
      return null;
    //  the conversion from one unique identifier to an other is
    //  a question of formating : the last "-" is placed
    StringBuffer buffer = new StringBuffer(entryUid);
@@ -1355,5 +1327,106 @@
    return buffer.toString();
  }
}
  /**
   * Traverse the provided search filter, looking for some conditions
   * on attributes that can be optimized in the ECL.
   * When found, populate the provided StartECLSessionMsg.
   * @param startCLmsg the startCLMsg to be populated.
   * @param sf the provided search filter.
   * @throws DirectoryException when an exception occurs.
   */
  public static void evaluateFilter(StartECLSessionMsg startCLmsg,
      SearchFilter sf)
  throws DirectoryException
  {
    StartECLSessionMsg msg = evaluateFilter2(sf);
    startCLmsg.setFirstDraftChangeNumber(msg.getFirstDraftChangeNumber());
    startCLmsg.setLastDraftChangeNumber(msg.getLastDraftChangeNumber());
    startCLmsg.setChangeNumber(msg.getChangeNumber());
  }
  private static StartECLSessionMsg evaluateFilter2(SearchFilter sf)
  throws DirectoryException
  {
    StartECLSessionMsg startCLmsg = new StartECLSessionMsg();
    startCLmsg.setFirstDraftChangeNumber(-1);
    startCLmsg.setLastDraftChangeNumber(-1);
    startCLmsg.setChangeNumber(new ChangeNumber(0,0,(short)0));
    // Here are the 3 elementary cases we know how to optimize
    if ((sf != null)
        && (sf.getFilterType() == FilterType.GREATER_OR_EQUAL)
        && (sf.getAttributeType() != null)
        && (sf.getAttributeType().getPrimaryName().
            equalsIgnoreCase("changeNumber")))
    {
      int sn = Integer.decode(
          sf.getAssertionValue().getNormalizedValue().toString());
      startCLmsg.setFirstDraftChangeNumber(sn);
      return startCLmsg;
    }
    else if ((sf != null)
        && (sf.getFilterType() == FilterType.LESS_OR_EQUAL)
        && (sf.getAttributeType() != null)
        && (sf.getAttributeType().getPrimaryName().
            equalsIgnoreCase("changeNumber")))
    {
      int sn = Integer.decode(
          sf.getAssertionValue().getNormalizedValue().toString());
      startCLmsg.setLastDraftChangeNumber(sn);
      return startCLmsg;
    }
    else if ((sf != null)
        && (sf.getFilterType() == FilterType.EQUALITY)
        && (sf.getAttributeType() != null)
        && (sf.getAttributeType().getPrimaryName().
            equalsIgnoreCase("replicationcsn")))
    {
      // == exact changenumber
      ChangeNumber cn = new ChangeNumber(sf.getAssertionValue().toString());
      startCLmsg.setChangeNumber(cn);
      return startCLmsg;
    }
    else if ((sf != null)
        && (sf.getFilterType() == FilterType.EQUALITY)
        && (sf.getAttributeType() != null)
        && (sf.getAttributeType().getPrimaryName().
            equalsIgnoreCase("changenumber")))
    {
      int sn = Integer.decode(
          sf.getAssertionValue().getNormalizedValue().toString());
      startCLmsg.setFirstDraftChangeNumber(sn);
      startCLmsg.setLastDraftChangeNumber(sn);
      return startCLmsg;
    }
    else if ((sf != null)
        && (sf.getFilterType() == FilterType.AND))
    {
      // 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]);
      int l1 = m1.getLastDraftChangeNumber();
      int l2 = m2.getLastDraftChangeNumber();
      if (l1 == -1)
        startCLmsg.setLastDraftChangeNumber(l2);
      else
        if (l2 == -1)
          startCLmsg.setLastDraftChangeNumber(l1);
        else
          startCLmsg.setLastDraftChangeNumber(Math.min(l1,l2));
      int f1 = m1.getFirstDraftChangeNumber();
      int f2 = m2.getFirstDraftChangeNumber();
      startCLmsg.setFirstDraftChangeNumber(Math.max(f1,f2));
      return startCLmsg;
    }
    else
    {
      return startCLmsg;
    }
  }
}