From f912fe883b2fd613b4115e29a2cf0c237a135b0c Mon Sep 17 00:00:00 2001
From: pgamba <pgamba@localhost>
Date: Wed, 12 Sep 2007 09:09:12 +0000
Subject: [PATCH] #793 / Ability to export replication backend

---
 opends/src/server/org/opends/server/replication/server/ServerWriter.java                                  |    2 
 opends/src/server/org/opends/server/replication/server/ReplicationServer.java                             |   14 +
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java          |    6 
 opends/src/server/org/opends/server/replication/protocol/AddMsg.java                                      |    9 
 opends/src/server/org/opends/server/replication/server/ReplicationBackend.java                            |  406 ++++++++++++++++++++++++++++++++++++
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java |  190 ++++++++++++++++
 opends/src/messages/messages/replication.properties                                                       |    4 
 opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java                                   |    2 
 opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java                                 |    2 
 9 files changed, 619 insertions(+), 16 deletions(-)

diff --git a/opends/src/messages/messages/replication.properties b/opends/src/messages/messages/replication.properties
index 2b6cbbf..177234b 100644
--- a/opends/src/messages/messages/replication.properties
+++ b/opends/src/messages/messages/replication.properties
@@ -229,4 +229,8 @@
  testing existence or creating the replication backend : %s
  SEVERE_ERR_DELETE_REPL_BACKEND_FAILED_90=An unexpected error occured when \
  deleting the replication backend : %s
+ SEVERE_ERR_EXPORT_CANNOT_WRITE_ENTRY_TO_LDIF_91=An error occured when \
+ exporting to LDIF the entry %s : %s
+SEVERE_ERR_BACKEND_CANNOT_CREATE_LDIF_WRITER_92 =An error occured when \
+ creating the LDIF writer to export backend : %s
  
\ No newline at end of file
diff --git a/opends/src/server/org/opends/server/replication/protocol/AddMsg.java b/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
index 307e619..5234eda 100644
--- a/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
@@ -288,4 +288,13 @@
   {
     parentUniqueId = uid;
   }
+
+  /**
+   * Get the parent unique id of this add msg.
+   * @return the parent unique id.
+   */
+  public String getParentUid()
+  {
+    return parentUniqueId;
+  }
 }
diff --git a/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java b/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java
index b6ba1de..5c3757d 100644
--- a/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java
@@ -235,7 +235,7 @@
   @Override
   public String toString()
   {
-    return ("Modify DN " + getDn() + " " + newRDN + " " + newSuperior + " " +
+    return ("MODDN " + getDn() + " " + newRDN + " " + newSuperior + " " +
             getChangeNumber());
   }
 
diff --git a/opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java b/opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java
index d9c185e..21167fb 100644
--- a/opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java
@@ -232,6 +232,6 @@
   @Override
   public String toString()
   {
-    return("Modify " + getDn() + " " + getChangeNumber());
+    return("MOD " + getDn() + " " + getChangeNumber());
   }
 }
diff --git a/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java b/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
index d02c2c7..5ce0a43 100644
--- a/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
+++ b/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
@@ -26,12 +26,23 @@
  */
 package org.opends.server.replication.server;
 import static org.opends.messages.BackendMessages.*;
+import static org.opends.messages.JebMessages.INFO_JEB_EXPORT_FINAL_STATUS;
+import static org.opends.messages.JebMessages.INFO_JEB_EXPORT_PROGRESS_REPORT;
+import static org.opends.messages.ReplicationMessages.*;
+import static org.opends.server.loggers.ErrorLogger.logError;
 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
 import static org.opends.server.loggers.debug.DebugLogger.getTracer;
 import static org.opends.server.util.StaticUtils.getExceptionMessage;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
 
 import org.opends.messages.Message;
 import org.opends.server.admin.Configuration;
@@ -47,6 +58,15 @@
 import org.opends.server.core.ModifyOperation;
 import org.opends.server.core.SearchOperation;
 import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.replication.protocol.AddMsg;
+import org.opends.server.replication.protocol.DeleteMsg;
+import org.opends.server.replication.protocol.ModifyDNMsg;
+import org.opends.server.replication.protocol.ModifyMsg;
+import org.opends.server.replication.protocol.UpdateMessage;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
 import org.opends.server.types.BackupConfig;
 import org.opends.server.types.BackupDirectory;
 import org.opends.server.types.ConditionResult;
@@ -58,8 +78,14 @@
 import org.opends.server.types.LDIFExportConfig;
 import org.opends.server.types.LDIFImportConfig;
 import org.opends.server.types.LDIFImportResult;
+import org.opends.server.types.RawAttribute;
 import org.opends.server.types.RestoreConfig;
 import org.opends.server.types.ResultCode;
+import org.opends.server.util.AddChangeRecordEntry;
+import org.opends.server.util.DeleteChangeRecordEntry;
+import org.opends.server.util.LDIFWriter;
+import org.opends.server.util.ModifyChangeRecordEntry;
+import org.opends.server.util.ModifyDNChangeRecordEntry;
 import org.opends.server.util.Validator;
 
 /**
@@ -83,6 +109,8 @@
    */
   private static final DebugTracer TRACER = getTracer();
 
+  private static final String EXPORT_BASE_DN = "dc=replicationChanges";
+
   // The base DNs for this backend.
   private DN[] baseDNs;
 
@@ -109,6 +137,21 @@
   private JEBackendCfg cfg;
 
   /**
+   * The number of milliseconds between job progress reports.
+   */
+  private long progressInterval = 10000;
+
+  /**
+   * The current number of entries exported.
+   */
+  private long exportedCount = 0;
+
+  /**
+   * The current number of entries skipped.
+   */
+  private long skippedCount = 0;
+
+  /**
    * Creates a new backend with the provided information.  All backend
    * implementations must implement a default constructor that use
    * <CODE>super()</CODE> to invoke this constructor.
@@ -345,16 +388,323 @@
    */
   public boolean supportsLDIFExport()
   {
-    return false;
+    return true;
   }
 
   /**
    * {@inheritDoc}
    */
   public synchronized void exportLDIF(LDIFExportConfig exportConfig)
-         throws DirectoryException
+  throws DirectoryException
   {
-    // TODO
+    List<DN> includeBranches = exportConfig.getIncludeBranches();
+    DN baseDN;
+    ArrayList<ReplicationCache> exportContainers =
+      new ArrayList<ReplicationCache>();
+
+    Iterator<ReplicationCache> rcachei = server.getCacheIterator();
+    if (rcachei != null)
+    {
+      while (rcachei.hasNext())
+      {
+        ReplicationCache rc = rcachei.next();
+
+        // Skip containers that are not covered by the include branches.
+        baseDN = DN.decode(rc.getBaseDn().toString() + "," + EXPORT_BASE_DN);
+
+        if (includeBranches == null || includeBranches.isEmpty())
+        {
+          exportContainers.add(rc);
+        }
+        else
+        {
+          for (DN includeBranch : includeBranches)
+          {
+            if (includeBranch.isDescendantOf(baseDN) ||
+                includeBranch.isAncestorOf(baseDN))
+            {
+              exportContainers.add(rc);
+            }
+          }
+        }
+      }
+    }
+
+    // Make a note of the time we started.
+    long startTime = System.currentTimeMillis();
+
+    // Start a timer for the progress report.
+    Timer timer = new Timer();
+    TimerTask progressTask = new ProgressTask();
+    timer.scheduleAtFixedRate(progressTask, progressInterval,
+        progressInterval);
+
+    // Create the LDIF writer.
+    LDIFWriter ldifWriter;
+    try
+    {
+      ldifWriter = new LDIFWriter(exportConfig);
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message =
+        ERR_BACKEND_CANNOT_CREATE_LDIF_WRITER.get(String.valueOf(e));
+      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+          message, e);
+    }
+
+    exportRootChanges(exportContainers, exportConfig, ldifWriter);
+
+    // Iterate through the containers.
+    try
+    {
+      for (ReplicationCache exportContainer : exportContainers)
+      {
+        exportContainer(exportContainer, exportConfig, ldifWriter);
+      }
+    }
+    finally
+    {
+      timer.cancel();
+
+      // Close the LDIF writer
+      try
+      {
+        ldifWriter.close();
+      }
+      catch (Exception e)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+      }
+    }
+
+    long finishTime = System.currentTimeMillis();
+    long totalTime = (finishTime - startTime);
+
+    float rate = 0;
+    if (totalTime > 0)
+    {
+      rate = 1000f*exportedCount / totalTime;
+    }
+
+    Message message = INFO_JEB_EXPORT_FINAL_STATUS.get(
+        exportedCount, skippedCount, totalTime/1000, rate);
+    logError(message);
+  }
+
+  /*
+   * Exports the root changes of the export, and one entry by domain.
+   */
+  private void exportRootChanges(List<ReplicationCache> exportContainers,
+      LDIFExportConfig exportConfig, LDIFWriter ldifWriter)
+  {
+    Map<AttributeType,List<Attribute>> attributes =
+      new HashMap<AttributeType,List<Attribute>>();
+    ArrayList<Attribute> ldapAttrList = new ArrayList<Attribute>();
+
+    AttributeType ocType=
+      DirectoryServer.getAttributeType("objectclass", true);
+    LinkedHashSet<AttributeValue> ocValues =
+      new LinkedHashSet<AttributeValue>();
+    ocValues.add(new AttributeValue(ocType, "top"));
+    ocValues.add(new AttributeValue(ocType, "domain"));
+    Attribute ocAttr = new Attribute(ocType, "objectclass", ocValues);
+    ldapAttrList.add(ocAttr);
+    attributes.put(ocType, ldapAttrList);
+
+    try
+    {
+      AddChangeRecordEntry changeRecord =
+        new AddChangeRecordEntry(DN.decode(EXPORT_BASE_DN),
+                               attributes);
+      ldifWriter.writeChangeRecord(changeRecord);
+    }
+    catch (Exception e) {}
+
+    for (ReplicationCache exportContainer : exportContainers)
+    {
+      attributes.clear();
+      ldapAttrList.clear();
+
+      ldapAttrList.add(ocAttr);
+
+      AttributeType stateType=
+        DirectoryServer.getAttributeType("state", true);
+      LinkedHashSet<AttributeValue> stateValues =
+        new LinkedHashSet<AttributeValue>();
+      stateValues.add(new AttributeValue(stateType,
+          exportContainer.getDbServerState().toString()));
+      TRACER.debugInfo("State=" +
+          exportContainer.getDbServerState().toString());
+      Attribute stateAttr = new Attribute(ocType, "state", stateValues);
+      ldapAttrList.add(stateAttr);
+
+      AttributeType genidType=
+        DirectoryServer.getAttributeType("generation-id", true);
+      LinkedHashSet<AttributeValue> genidValues =
+        new LinkedHashSet<AttributeValue>();
+      genidValues.add(new AttributeValue(genidType,
+          String.valueOf(exportContainer.getGenerationId())+
+          exportContainer.getBaseDn()));
+      Attribute genidAttr = new Attribute(ocType, "generation-id", genidValues);
+      ldapAttrList.add(genidAttr);
+      attributes.put(genidType, ldapAttrList);
+
+      try
+      {
+        AddChangeRecordEntry changeRecord =
+          new AddChangeRecordEntry(DN.decode(
+              exportContainer.getBaseDn() + "," + EXPORT_BASE_DN),
+              attributes);
+        ldifWriter.writeChangeRecord(changeRecord);
+      }
+      catch (Exception e)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+        Message message = ERR_EXPORT_CANNOT_WRITE_ENTRY_TO_LDIF.get(
+            exportContainer.getBaseDn() + "," + EXPORT_BASE_DN,
+            String.valueOf(e));
+        logError(message);
+      }
+    }
+  }
+
+  /**
+   * Export the changes for a given ReplicationCache.
+   */
+  private void exportContainer(ReplicationCache rc,
+      LDIFExportConfig exportConfig, LDIFWriter ldifWriter)
+  {
+    StringBuilder buffer = new StringBuilder();
+
+    // Walk through the servers
+    for (Short serverId : rc.getServers())
+    {
+      ReplicationIterator ri = rc.getChangelogIterator(serverId,
+          null);
+
+      if (ri == null)
+        break;
+
+      // Walk through the changes
+      while (ri.getChange() != null)
+      {
+        UpdateMessage msg = ri.getChange();
+        exportChange(buffer, msg, exportConfig, ldifWriter);
+        if (!ri.next())
+          break;
+      }
+    }
+  }
+
+  /**
+   * Export one change.
+   */
+  private void exportChange(StringBuilder buffer, UpdateMessage msg,
+      LDIFExportConfig exportConfig, LDIFWriter ldifWriter)
+  {
+    InternalClientConnection conn =
+      InternalClientConnection.getRootConnection();
+    String dn = null;
+    try
+    {
+      if (msg instanceof AddMsg)
+      {
+        AddMsg addMsg = (AddMsg)msg;
+        AddOperation op = (AddOperation)msg.createOperation(conn);
+        dn = "puid=" + addMsg.getParentUid() + "," +
+             "changeNumber=" + msg.getChangeNumber().toString() + "," +
+             msg.getDn() +","+ "dc=replicationChanges";
+
+        Map<AttributeType,List<Attribute>> attributes =
+          new HashMap<AttributeType,List<Attribute>>();
+
+        for (RawAttribute a : op.getRawAttributes())
+        {
+          Attribute attr = a.toAttribute();
+          AttributeType attrType = attr.getAttributeType();
+          List<Attribute> attrs = attributes.get(attrType);
+          if (attrs == null)
+          {
+            attrs = new ArrayList<Attribute>(1);
+            attrs.add(attr);
+            attributes.put(attrType, attrs);
+          }
+          else
+          {
+            attrs.add(attr);
+          }
+        }
+        AddChangeRecordEntry changeRecord =
+          new AddChangeRecordEntry(DN.decode(dn), attributes);
+        ldifWriter.writeChangeRecord(changeRecord);
+      }
+      else if (msg instanceof DeleteMsg)
+      {
+        DeleteMsg delMsg = (DeleteMsg)msg;
+        // DN
+        dn = "uuid=" + msg.getUniqueId() + "," +
+        "changeNumber=" + delMsg.getChangeNumber().toString()+ "," +
+        msg.getDn() +","+
+        "dc=replicationChanges";
+        DeleteChangeRecordEntry changeRecord =
+          new DeleteChangeRecordEntry(DN.decode(dn));
+        ldifWriter.writeChangeRecord(changeRecord);
+      }
+      else if (msg instanceof ModifyMsg)
+      {
+        ModifyOperation op = (ModifyOperation)msg.createOperation(conn);
+        // DN
+        dn = "uuid=" + msg.getUniqueId() + "," +
+        "changeNumber=" + msg.getChangeNumber().toString()+ "," +
+        msg.getDn() +","+
+        "dc=replicationChanges";
+        op.setInternalOperation(true);
+        ModifyChangeRecordEntry changeRecord =
+          new ModifyChangeRecordEntry(DN.decode(dn),
+              op.getRawModifications());
+        ldifWriter.writeChangeRecord(changeRecord);
+      }
+      else if (msg instanceof ModifyDNMsg)
+      {
+        ModifyDNOperation op = (ModifyDNOperation)msg.createOperation(conn);
+        // DN
+        dn = "uuid=" + msg.getUniqueId() + "," +
+        "changeNumber=" + msg.getChangeNumber().toString()+ "," +
+        msg.getDn() +","+
+        "dc=replicationChanges";
+        op.setInternalOperation(true);
+        ModifyDNChangeRecordEntry changeRecord =
+          new ModifyDNChangeRecordEntry(DN.decode(dn),
+              op.getNewRDN(), op.deleteOldRDN(),
+              op.getNewSuperior());
+        ldifWriter.writeChangeRecord(changeRecord);
+      }
+      this.exportedCount++;
+    }
+    catch (Exception e)
+    {
+      this.skippedCount++;
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+      Message message = ERR_EXPORT_CANNOT_WRITE_ENTRY_TO_LDIF.get(
+          dn, String.valueOf(e));
+      logError(message);
+
+    }
   }
 
   /**
@@ -490,4 +840,54 @@
   {
     this.server = server;
   }
+
+  /**
+   * This class reports progress of the export job at fixed intervals.
+   */
+  class ProgressTask extends TimerTask
+  {
+    /**
+     * The number of entries that had been exported at the time of the
+     * previous progress report.
+     */
+    private long previousCount = 0;
+
+    /**
+     * The time in milliseconds of the previous progress report.
+     */
+    private long previousTime;
+
+    /**
+     * Create a new export progress task.
+     */
+    public ProgressTask()
+    {
+      previousTime = System.currentTimeMillis();
+    }
+
+    /**
+     * The action to be performed by this timer task.
+     */
+    public void run()
+    {
+      long latestCount = exportedCount;
+      long deltaCount = (latestCount - previousCount);
+      long latestTime = System.currentTimeMillis();
+      long deltaTime = latestTime - previousTime;
+
+      if (deltaTime == 0)
+      {
+        return;
+      }
+
+      float rate = 1000f*deltaCount / deltaTime;
+
+      Message message =
+          INFO_JEB_EXPORT_PROGRESS_REPORT.get(latestCount, skippedCount, rate);
+      logError(message);
+
+      previousCount = latestCount;
+      previousTime = latestTime;
+    }
+  };
 }
diff --git a/opends/src/server/org/opends/server/replication/server/ReplicationServer.java b/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
index 8061015..7a445bf 100644
--- a/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
+++ b/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
@@ -43,6 +43,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
@@ -940,4 +941,17 @@
   {
     // Nothing is needed at the moment
   }
+
+  /**
+   * Returns an iterator on the list of replicationCache.
+   * Returns null if none.
+   * @return the iterator.
+   */
+  public Iterator<ReplicationCache> getCacheIterator()
+  {
+    if (!baseDNs.isEmpty())
+      return baseDNs.values().iterator();
+    else
+      return null;
+  }
 }
diff --git a/opends/src/server/org/opends/server/replication/server/ServerWriter.java b/opends/src/server/org/opends/server/replication/server/ServerWriter.java
index b051836..3fd0503 100644
--- a/opends/src/server/org/opends/server/replication/server/ServerWriter.java
+++ b/opends/src/server/org/opends/server/replication/server/ServerWriter.java
@@ -122,7 +122,7 @@
             "In " + replicationCache.getReplicationServer().
               getMonitorInstanceName() +
             ", writer to " + this.handler.getMonitorInstanceName() +
-            " publishes" + update.toString() +
+            " publishes msg=" + update.toString() +
             " refgenId=" + referenceGenerationId +
             " server=" + handler.getServerId() +
             " generationId=" + handler.getGenerationId());
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
index a09b5e7..566dda4 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
@@ -855,8 +855,9 @@
         {
           fail("No log messages were written to the task entry on a failed task");
         }
-        else
-        {
+      }
+      if (logMessages.size() != 0)
+      {
           TRACER.debugInfo(logMessages.get(0));
           if (expectedMessage != null)
           {
@@ -864,7 +865,6 @@
             assertTrue(logMessages.get(0).indexOf(
                 expectedMessage.toString())>0);
           }
-        }
       }
 
       assertEquals(taskState, expectedTaskState, "Task State:" + taskState +
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java
index a16d3f8..5d350b9 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java
@@ -26,13 +26,12 @@
  */
 package org.opends.server.replication.server;
 
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.fail;
-
 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
 import static org.opends.server.loggers.debug.DebugLogger.getTracer;
-import static org.opends.server.replication.protocol.OperationContext.*;
+import static org.opends.server.replication.protocol.OperationContext.SYNCHROCONTEXT;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 import java.io.File;
 import java.net.InetAddress;
@@ -54,14 +53,27 @@
 import org.opends.server.replication.common.ChangeNumberGenerator;
 import org.opends.server.replication.common.ServerState;
 import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.protocol.*;
+import org.opends.server.replication.protocol.AddMsg;
+import org.opends.server.replication.protocol.DeleteMsg;
+import org.opends.server.replication.protocol.ModifyDNMsg;
+import org.opends.server.replication.protocol.ModifyDnContext;
+import org.opends.server.replication.protocol.ModifyMsg;
+import org.opends.server.replication.protocol.ProtocolSession;
+import org.opends.server.replication.protocol.ProtocolVersion;
+import org.opends.server.replication.protocol.ReplServerStartMessage;
+import org.opends.server.replication.protocol.ReplSessionSecurity;
+import org.opends.server.replication.protocol.ReplicationMessage;
+import org.opends.server.replication.protocol.ServerStartMessage;
+import org.opends.server.replication.protocol.UpdateMessage;
+import org.opends.server.replication.protocol.WindowMessage;
+import org.opends.server.replication.protocol.WindowProbe;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryConfig;
 import org.opends.server.types.Entry;
 import org.opends.server.types.Modification;
 import org.opends.server.types.ModificationType;
 import org.opends.server.types.RDN;
-import org.opends.server.types.DirectoryConfig;
 import org.opends.server.types.ResultCode;
 import org.opends.server.util.TimeThread;
 import org.opends.server.workflowelement.localbackend.LocalBackendModifyDNOperation;
@@ -995,6 +1007,56 @@
      debugInfo("Ending   backupRestore");
    }
 
+   /* 
+    * Test export of the Replication server backend
+    * - Creates 2 brokers connecting to the replication for 2 differents baseDN
+    * - Make these brokers publish changes to the replication server
+    * - Launch a full export
+    * - Launch a partial export on one of the 2 domains
+    */
+    @Test(enabled=true)
+    public void exportBackend() throws Exception
+    {
+      debugInfo("Starting exportBackend");
+      
+      ReplicationBroker server1 = null;
+      ReplicationBroker server2 = null;
+
+      try {
+        server1 = openReplicationSession(
+            DN.decode("dc=example,dc=com"), (short) 1, 100, replicationServerPort,
+            1000, true);
+        server2 = openReplicationSession(
+            DN.decode("dc=example2,dc=com"), (short) 2, 100, replicationServerPort,
+            1000, true);
+      }
+      catch(Exception e) {}
+      
+      debugInfo("Publish changes");
+      List<UpdateMessage> msgs = createChanges("dc=example,dc=com", (short)1);
+      for(UpdateMessage msg : msgs )
+      {
+        server1.publish(msg);
+      }
+      List<UpdateMessage> msgs2 = createChanges("dc=example2,dc=com", (short)2);
+      for(UpdateMessage msg : msgs2 )
+      {
+        server2.publish(msg);
+      }
+
+      debugInfo("Export all");
+      Entry exportTask = createExportAllTask();
+      addTask(exportTask, ResultCode.SUCCESS, null);
+      waitTaskState(exportTask, TaskState.COMPLETED_SUCCESSFULLY, null);
+
+      debugInfo("Export domain");
+      exportTask = createExportDomainTask("dc=example2,dc=com");
+      addTask(exportTask, ResultCode.SUCCESS, null);
+      waitTaskState(exportTask, TaskState.COMPLETED_SUCCESSFULLY, null);
+
+      debugInfo("Ending export");
+    }
+
    private Entry createBackupTask()
    throws Exception
    {
@@ -1022,4 +1084,118 @@
      "ds-backup-directory-path: bak" + File.separator +
                         "replicationChanges");
    }
+   
+   private Entry createExportAllTask()
+   throws Exception
+   {
+     String buildRoot = System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT);
+     String path = buildRoot + File.separator + "build" +
+                   File.separator + "unit-tests" + File.separator +
+                   "package"+ File.separator + "exportLDIF.ldif";
+     return TestCaseUtils.makeEntry(
+     "dn: ds-task-id=" + UUID.randomUUID() + ",cn=Scheduled Tasks,cn=Tasks",
+     "objectclass: top",
+     "objectclass: ds-task",
+     "objectclass: ds-task-export",
+     "ds-task-class-name: org.opends.server.tasks.ExportTask",
+     "ds-task-export-ldif-file: " + path,
+     "ds-task-export-backend-id: replicationChanges",
+     "ds-task-export-include-branch: dc=replicationChanges");
+   }
+
+   private Entry createExportDomainTask(String suffix)
+   throws Exception
+   {
+     String root = suffix.substring(suffix.indexOf('=')+1, suffix.indexOf(','));
+     String buildRoot = System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT);
+     String path = buildRoot + File.separator + "build" +
+                   File.separator + "unit-tests" + File.separator +
+                   "package"+ File.separator + "exportLDIF" + root +".ldif";
+     return TestCaseUtils.makeEntry(
+     "dn: ds-task-id=" + UUID.randomUUID() + ",cn=Scheduled Tasks,cn=Tasks",
+     "objectclass: top",
+     "objectclass: ds-task",
+     "objectclass: ds-task-export",
+     "ds-task-class-name: org.opends.server.tasks.ExportTask",
+     "ds-task-export-ldif-file: " + path,
+     "ds-task-export-backend-id: replicationChanges",
+     "ds-task-export-include-branch: "+suffix+",dc=replicationChanges");
+   }
+
+   private List<UpdateMessage> createChanges(String suffix, short serverId)
+   {
+     List<UpdateMessage> l = new ArrayList<UpdateMessage>();
+     long time = TimeThread.getTime();
+     int ts = 1;
+     ChangeNumber cn;
+
+     try
+     {
+       String user1entryUUID = "33333333-3333-3333-3333-333333333333";
+       String baseUUID       = "22222222-2222-2222-2222-222222222222";
+
+       // - Add
+       String lentry = new String("dn: "+suffix+"\n"
+           + "objectClass: top\n" 
+           + "objectClass: domain\n"
+           + "entryUUID: 11111111-1111-1111-1111-111111111111\n");
+       Entry entry = TestCaseUtils.entryFromLdifString(lentry);
+       cn = new ChangeNumber(time, ts++, serverId);
+       AddMsg addMsg = new AddMsg(cn, "o=test,"+suffix,
+           user1entryUUID, baseUUID, entry.getObjectClassAttribute(), entry
+           .getAttributes(), new ArrayList<Attribute>());
+       l.add(addMsg);
+
+       // - Add
+       String luentry = new String(
+             "dn: uid=new person,ou=People,"+suffix+"\n"
+           + "objectClass: top\n" 
+           + "objectclass: person\n"
+           + "objectclass: organizationalPerson\n"
+           + "objectclass: inetOrgPerson\n"
+           + "cn: Fiona Jensen\n"
+           + "sn: Jensen\n"
+           + "uid: new person\n"
+           + "telephonenumber: +1 408 555 1212\n"
+           + "entryUUID: " + user1entryUUID +"\n");
+       Entry uentry = TestCaseUtils.entryFromLdifString(luentry);
+       cn = new ChangeNumber(time, ts++, serverId);
+       AddMsg addMsg2 = new AddMsg(
+           cn, 
+           "uid=new person,ou=People,"+suffix,
+           user1entryUUID, 
+           baseUUID, 
+           uentry.getObjectClassAttribute(), 
+           uentry.getAttributes(), 
+           new ArrayList<Attribute>());
+       l.add(addMsg2);
+
+       // - Modify
+       Attribute attr1 = new Attribute("description", "new value");
+       Modification mod1 = new Modification(ModificationType.REPLACE, attr1);
+       List<Modification> mods = new ArrayList<Modification>();
+       mods.add(mod1);
+       cn = new ChangeNumber(time, ts++, serverId);
+       DN dn = DN.decode("o=test,"+suffix);
+       ModifyMsg modMsg = new ModifyMsg(cn, dn, 
+           mods, "fakeuniqueid");
+       l.add(modMsg);
+
+       // Modify DN
+       cn = new ChangeNumber(time, ts++, serverId);
+       ModifyDNMsg  modDnMsg = new ModifyDNMsg(
+           "uid=new person,ou=People,"+suffix, cn,
+           user1entryUUID, baseUUID, false,
+           "uid=wrong, ou=people,"+suffix,
+       "uid=newrdn");
+       l.add(modDnMsg);
+
+       // Del
+       cn = new ChangeNumber(time, ts++, serverId);
+       DeleteMsg delMsg = new DeleteMsg("o=test,"+suffix, cn, "uid");
+       l.add(delMsg);
+     }
+     catch(Exception e) {};
+     return l;
+   }
 }

--
Gitblit v1.10.0