From 7adb93986ace907531875e25be1f94d735fbb068 Mon Sep 17 00:00:00 2001
From: gbellato <gbellato@localhost>
Date: Mon, 08 Dec 2008 08:03:33 +0000
Subject: [PATCH] Merge the replication-service branch with the OpenDS trunk
---
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PendingChange.java | 10
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java | 45
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java | 1874 +------
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java | 68
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java | 80
opendj-sdk/opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliMain.java | 4
opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationMonitor.java | 331 +
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/MonitorTest.java | 9
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PendingChanges.java | 48
opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java | 672 ++
opendj-sdk/opends/src/server/org/opends/server/replication/server/SafeDataExpectedAcksInfo.java | 110
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalCsnOrderingTest.java | 11
opendj-sdk/opends/src/server/org/opends/server/replication/server/SafeReadExpectedAcksInfo.java | 240 +
opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServer.java | 20
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java | 492 ++
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java | 18
opendj-sdk/opends/resource/config/config.ldif | 6
opendj-sdk/opends/src/server/org/opends/server/replication/service/package-info.java | 26
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java | 56
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeStressReplicationDomain.java | 159
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/InitializeTargetMsg.java | 19
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java | 40
opendj-sdk/opends/src/server/org/opends/server/replication/service/InitializeTargetTask.java | 45
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentServerStateTest.java | 12
opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplInputStream.java | 10
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ModifyConflictTest.java | 7
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java | 72
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ServerStartMsg.java | 16
opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplOutputStream.java | 82
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationServerFailoverTest.java | 12
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java | 66
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java | 149
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java | 317 +
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/UpdateMsg.java | 596 -
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplayThread.java | 7
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java | 10
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalTest.java | 12
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/HeartbeatMonitor.java | 3
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java | 24
opendj-sdk/opends/src/server/org/opends/server/replication/server/NotAssuredUpdateMsg.java | 358 +
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerDynamicConfTest.java | 4
opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java | 2433 +++++++++++
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ComputeBestServerTest.java | 52
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ProtocolWindowTest.java | 3
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReSyncTest.java | 13
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/StateMachineTest.java | 159
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java | 6
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java | 20
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/IsolationTest.java | 11
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/InitializeRequestMsg.java | 4
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java | 20
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java | 2
opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerHandler.java | 313 -
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java | 59
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java | 7
opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerReader.java | 8
opendj-sdk/opends/src/server/org/opends/server/replication/common/ServerStatus.java | 27
opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java | 280
opendj-sdk/opends/src/server/org/opends/server/replication/service/ListenerThread.java | 38
opendj-sdk/opends/src/server/org/opends/server/replication/common/ServerState.java | 31
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/GenerationIdTest.java | 325 -
opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java | 2
opendj-sdk/opends/src/server/org/opends/server/replication/service/SetGenerationIdTask.java | 32
opendj-sdk/opends/src/server/org/opends/server/replication/server/DbHandler.java | 5
opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationDB.java | 5
opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerWriter.java | 6
opendj-sdk/opends/src/messages/messages/replication.properties | 19
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java | 141
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/DbHandlerTest.java | 5
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplLDIFOutputStream.java | 4
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java | 2
opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationDbEnv.java | 53
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/AssuredReplicationPluginTest.java | 1535 +++++++
opendj-sdk/opends/src/server/org/opends/server/replication/server/StatusAnalyzer.java | 2
opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java | 450 -
opendj-sdk/opends/src/server/org/opends/server/replication/service/InitializeTask.java | 37
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ChangeNumberControlPluginTestCase.java | 44
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/WindowMsg.java | 3
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/GroupIdHandshakeTest.java | 24
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/UpdateToReplay.java | 14
opendj-sdk/opends/src/server/org/opends/server/replication/server/MonitorData.java | 66
/dev/null | 84
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/FakeModdnOperation.java | 2
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/StressTest.java | 4
opendj-sdk/opends/src/server/org/opends/server/replication/server/ExpectedAcksInfo.java | 141
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AddMsg.java | 2
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/EntryMsg.java | 29
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/FakeAddOperation.java | 2
opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AckMsg.java | 67
89 files changed, 8,912 insertions(+), 3,819 deletions(-)
diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index 2107319..f782b11 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -58,9 +58,9 @@
ds-cfg-allowed-task: org.opends.server.tasks.EnterLockdownModeTask
ds-cfg-allowed-task: org.opends.server.tasks.ExportTask
ds-cfg-allowed-task: org.opends.server.tasks.ImportTask
-ds-cfg-allowed-task: org.opends.server.tasks.InitializeTargetTask
-ds-cfg-allowed-task: org.opends.server.tasks.InitializeTask
-ds-cfg-allowed-task: org.opends.server.tasks.SetGenerationIdTask
+ds-cfg-allowed-task: org.opends.server.replication.service.InitializeTargetTask
+ds-cfg-allowed-task: org.opends.server.replication.service.InitializeTask
+ds-cfg-allowed-task: org.opends.server.replication.service.SetGenerationIdTask
ds-cfg-allowed-task: org.opends.server.tasks.LeaveLockdownModeTask
ds-cfg-allowed-task: org.opends.server.tasks.RebuildTask
ds-cfg-allowed-task: org.opends.server.tasks.RestoreTask
diff --git a/opendj-sdk/opends/src/messages/messages/replication.properties b/opendj-sdk/opends/src/messages/messages/replication.properties
index d405c94..b519ed2 100644
--- a/opendj-sdk/opends/src/messages/messages/replication.properties
+++ b/opendj-sdk/opends/src/messages/messages/replication.properties
@@ -109,8 +109,9 @@
connection
SEVERE_ERR_WRITER_UNEXPECTED_EXCEPTION_32=An unexpected error happened \
handling connection with %s. This connection is going to be closed
-SEVERE_ERR_CHANGELOG_ERROR_SENDING_ACK_33=An unexpected error occurred while \
- sending an ack to %s. This connection is going to be closed and reopened
+SEVERE_ERR_RS_ERROR_SENDING_ACK_33=In replication server %s: an unexpected error \
+ occurred while sending an ack to server id %s for change number %s in domain %s \
+ . This connection is going to be closed and reopened
SEVERE_ERR_EXCEPTION_RECEIVING_REPLICATION_MESSAGE_34=An Exception was caught \
while receiving replication message : %s
MILD_ERR_LOOP_REPLAYING_OPERATION_35=A loop was detected while replaying \
@@ -269,7 +270,7 @@
DEBUG_SENDING_CHANGE_112=Sending change number: %s
DEBUG_CHANGES_SENT_113=All missing changes sent to replication server
SEVERE_ERR_PUBLISHING_FAKE_OPS_114=Caught exception publishing fake operations \
-for domain %s to replication server %s : %s
+for domain %s : %s
SEVERE_ERR_COMPUTING_FAKE_OPS_115=Caught exception computing fake operations \
for domain %s for replication server %s : %s
NOTICE_IGNORING_REMOTE_MONITOR_DATA_116=Some monitor data have been received \
@@ -346,3 +347,15 @@
server %s will be aborted. Simultanate cross connection attempt ?
NOTICE_BAD_GENERATION_ID_FROM_DS_146=On suffix %s, directory server %s presented \
generation ID=%s when expected generation ID=%s
+SEVERE_ERR_DS_RECEIVED_ACK_ERROR_147=In replication service %s, the assured \
+ update message %s was acknowledged with the following errors: %s
+SEVERE_ERR_DS_ACK_TIMEOUT_148=In replication service %s, timeout after %s ms \
+ waiting for the acknowledgement of the assured update message: %s
+SEVERE_ERR_DS_UNKNOWN_ASSURED_MODE_149=In directory server %s, received unknown \
+ assured update mode: %s, for domain %s. Message: %s
+SEVERE_ERR_RS_UNKNOWN_ASSURED_MODE_150=In replication server %s, received unknown \
+ assured update mode: %s, for domain %s. Message: %s
+SEVERE_ERR_UNKNOWN_ASSURED_SAFE_DATA_LEVEL_151=In replication server %s, \
+ received a safe data assured update message with incoherent level: %s, this is \
+ for domain %s. Message: %s
+
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
index 6b468d2..6dfe642 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
@@ -4204,7 +4204,9 @@
oc.add("ds-task");
oc.add("ds-task-initialize-from-remote-replica");
attrs.put(oc);
- attrs.put("ds-task-class-name", "org.opends.server.tasks.InitializeTask");
+ attrs.put(
+ "ds-task-class-name",
+ "org.opends.server.replication.service.InitializeTask");
attrs.put("ds-task-initialize-domain-dn", suffixDn);
attrs.put("ds-task-initialize-replica-server-id",
String.valueOf(replicaId));
@@ -4521,7 +4523,7 @@
oc.add("ds-task-reset-generation-id");
attrs.put(oc);
attrs.put("ds-task-class-name",
- "org.opends.server.tasks.SetGenerationIdTask");
+ "org.opends.server.replication.service.SetGenerationIdTask");
attrs.put("ds-task-reset-generation-id-domain-base-dn", suffixDn);
while (!taskCreated)
{
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/common/ServerState.java b/opendj-sdk/opends/src/server/org/opends/server/replication/common/ServerState.java
index 81d2ac3..559ae24 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/common/ServerState.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/common/ServerState.java
@@ -49,6 +49,7 @@
public class ServerState implements Iterable<Short>
{
private HashMap<Short, ChangeNumber> list;
+ private boolean saved = true;
/**
* Creates a new empty ServerState.
@@ -142,16 +143,18 @@
/**
* Update the Server State with a ChangeNumber.
- * All operations with smaller CSN and the same serverID must be committed
- * before calling this method.
- * @param changeNumber the committed ChangeNumber.
- * @return a boolean indicating if the update was meaningfull.
+ *
+ * @param changeNumber The committed ChangeNumber.
+ *
+ * @return a boolean indicating if the update was meaningful.
*/
public boolean update(ChangeNumber changeNumber)
{
if (changeNumber == null)
return false;
+ saved = false;
+
synchronized(this)
{
Short id = changeNumber.getServerId();
@@ -395,4 +398,24 @@
return diff;
}
+
+ /**
+ * Set the saved status of this ServerState.
+ *
+ * @param b A booelan indicating if the State has been safely stored.
+ */
+ public void setSaved(boolean b)
+ {
+ saved = false;
+ }
+
+ /**
+ * Get the saved status of this ServerState.
+ *
+ * @return The saved status of this ServerState.
+ */
+ public boolean isSaved()
+ {
+ return saved;
+ }
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/common/ServerStatus.java b/opendj-sdk/opends/src/server/org/opends/server/replication/common/ServerStatus.java
index d46ab82..563c4b9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/common/ServerStatus.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/common/ServerStatus.java
@@ -131,4 +131,31 @@
{
return value;
}
+
+ /**
+ * Get a user readable string representing this status (User friendly string
+ * for monitoring purpose).
+ * @return A user readable string representing this status.
+ */
+ public String toString()
+ {
+ switch (value)
+ {
+ case -1:
+ return "Invalid";
+ case 0:
+ return "Not connected";
+ case 1:
+ return "Normal";
+ case 2:
+ return "Degraded";
+ case 3:
+ return "Full update";
+ case 4:
+ return "Bad generation id";
+ default:
+ throw new IllegalArgumentException("Wrong status numeric value: " +
+ value);
+ }
+ }
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/FakeAddOperation.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/FakeAddOperation.java
index 9a99c4a..65dd36b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/FakeAddOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/FakeAddOperation.java
@@ -62,7 +62,7 @@
{
return new AddMsg(getChangeNumber(), entry.getDN().toString(),
Historical.getEntryUuid(entry),
- ReplicationDomain.findEntryId(
+ LDAPReplicationDomain.findEntryId(
entry.getDN().getParentDNInSuffix()),
entry.getObjectClasses(),
entry.getUserAttributes(), entry.getOperationalAttributes());
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/FakeModdnOperation.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/FakeModdnOperation.java
index 15fd6bc..b750cea 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/FakeModdnOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/FakeModdnOperation.java
@@ -66,7 +66,7 @@
DN dn = entry.getDN();
return new ModifyDNMsg(dn.toString(), this.getChangeNumber(),
Historical.getEntryUuid(entry),
- ReplicationDomain.findEntryId(dn.getParent()),
+ LDAPReplicationDomain.findEntryId(dn.getParent()),
false, dn.getParent().toString(), dn.getRDN().toString());
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplicationDomain.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
similarity index 65%
rename from opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplicationDomain.java
rename to opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
index ed583c6..87cb100 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplicationDomain.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -25,6 +25,7 @@
* Copyright 2006-2008 Sun Microsystems, Inc.
*/
package org.opends.server.replication.plugin;
+
import static org.opends.messages.ReplicationMessages.*;
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.loggers.ErrorLogger.logError;
@@ -36,28 +37,31 @@
import static org.opends.server.util.StaticUtils.createEntry;
import static org.opends.server.util.StaticUtils.getFileForPath;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
-import static org.opends.server.replication.common.StatusMachine.*;
+
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
+
+import org.opends.server.replication.service.ReplicationMonitor;
+
+import java.util.Collection;
+
+import org.opends.server.types.Attributes;
import java.io.File;
-import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
-import java.net.SocketTimeoutException;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeMap;
+import java.util.Set;
+import java.util.TreeSet;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.CheckedOutputStream;
import java.util.zip.DataFormatException;
-
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.admin.server.ConfigurationChangeListener;
@@ -68,7 +72,6 @@
import org.opends.server.api.DirectoryThread;
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.backends.jeb.BackendImpl;
-import org.opends.server.backends.task.Task;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
@@ -82,49 +85,31 @@
import org.opends.server.protocols.asn1.ASN1Exception;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchListener;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.ldap.LDAPAttribute;
import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.protocols.ldap.LDAPModification;
+import org.opends.server.replication.service.ReplicationDomain;
import org.opends.server.replication.common.AssuredMode;
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.common.ChangeNumberGenerator;
-import org.opends.server.replication.common.DSInfo;
-import org.opends.server.replication.common.RSInfo;
import org.opends.server.replication.common.ServerState;
import org.opends.server.replication.common.ServerStatus;
-import org.opends.server.replication.common.StatusMachine;
-import org.opends.server.replication.common.StatusMachineEvent;
-import org.opends.server.replication.protocol.AckMsg;
import org.opends.server.replication.protocol.AddContext;
import org.opends.server.replication.protocol.AddMsg;
-import org.opends.server.replication.protocol.ChangeStatusMsg;
import org.opends.server.replication.protocol.DeleteContext;
-import org.opends.server.replication.protocol.DoneMsg;
-import org.opends.server.replication.protocol.EntryMsg;
-import org.opends.server.replication.protocol.ErrorMsg;
-import org.opends.server.replication.protocol.HeartbeatMsg;
-import org.opends.server.replication.protocol.InitializeRequestMsg;
-import org.opends.server.replication.protocol.InitializeTargetMsg;
import org.opends.server.replication.protocol.ModifyContext;
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.OperationContext;
-import org.opends.server.replication.protocol.ReplSessionSecurity;
-import org.opends.server.replication.protocol.ReplicationMsg;
-import org.opends.server.replication.protocol.ResetGenerationIdMsg;
-import org.opends.server.replication.protocol.RoutableMsg;
-import org.opends.server.replication.protocol.TopologyMsg;
+import org.opends.server.replication.protocol.ProtocolSession;
import org.opends.server.replication.protocol.UpdateMsg;
-import org.opends.server.tasks.InitializeTargetTask;
-import org.opends.server.tasks.InitializeTask;
import org.opends.server.tasks.TaskUtils;
-import org.opends.server.types.AttributeBuilder;
-import org.opends.server.types.Attributes;
-import org.opends.server.types.ExistingFileBehavior;
import org.opends.server.types.AbstractOperation;
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.ConfigChangeResult;
@@ -133,6 +118,7 @@
import org.opends.server.types.DereferencePolicy;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
+import org.opends.server.types.ExistingFileBehavior;
import org.opends.server.types.LDAPException;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
@@ -144,6 +130,7 @@
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchResultReference;
import org.opends.server.types.SearchScope;
import org.opends.server.types.SynchronizationProviderResult;
import org.opends.server.types.operation.PluginOperation;
@@ -163,9 +150,9 @@
* handle conflict resolution,
* handle protocol messages from the replicationServer.
*/
-public class ReplicationDomain extends DirectoryThread
+public class LDAPReplicationDomain extends ReplicationDomain
implements ConfigurationChangeListener<ReplicationDomainCfg>,
- AlertGenerator
+ AlertGenerator, InternalSearchListener
{
/**
* The fully-qualified name of this class.
@@ -185,37 +172,20 @@
*/
private static final DebugTracer TRACER = getTracer();
- private ReplicationMonitor monitor;
-
- private ReplicationBroker broker;
- // Thread waiting for incoming update messages for this domain and pushing
- // them to the global incoming update message queue for later processing by
- // replay threads.
- private ListenerThread listenerThread;
// The update to replay message queue where the listener thread is going to
// push incoming update messages.
private LinkedBlockingQueue<UpdateToReplay> updateToReplayQueue;
- private SortedMap<ChangeNumber, UpdateMsg> waitingAckMsgs =
- new TreeMap<ChangeNumber, UpdateMsg>();
- private AtomicInteger numRcvdUpdates = new AtomicInteger(0);
- private AtomicInteger numSentUpdates = new AtomicInteger(0);
- private AtomicInteger numProcessedUpdates = new AtomicInteger();
private AtomicInteger numResolvedNamingConflicts = new AtomicInteger();
private AtomicInteger numResolvedModifyConflicts = new AtomicInteger();
private AtomicInteger numUnresolvedNamingConflicts = new AtomicInteger();
private int debugCount = 0;
- private PersistentServerState state;
+ private final PersistentServerState state;
private int numReplayedPostOpCalled = 0;
- private int maxReceiveQueue = 0;
- private int maxSendQueue = 0;
- private int maxReceiveDelay = 0;
- private int maxSendDelay = 0;
-
private long generationId = -1;
private boolean generationIdSavedStatus = false;
- ChangeNumberGenerator generator;
+ private ChangeNumberGenerator generator;
/**
* This object is used to store the list of update currently being
@@ -235,19 +205,8 @@
*/
private RemotePendingChanges remotePendingChanges;
- /**
- * The time in milliseconds between heartbeats from the replication
- * server. Zero means heartbeats are off.
- */
- private long heartbeatInterval = 0;
private short serverId;
- // The context related to an import or export being processed
- // Null when none is being processed.
- private IEContext ieContext = null;
-
- private Collection<String> replicationServers;
-
private DN baseDn;
private boolean shutdown = false;
@@ -260,37 +219,10 @@
private boolean disabled = false;
private boolean stateSavingDisabled = false;
- private int window = 100;
-
- /*
- * Assured mode properties
- */
- // Is assured mode enabled or not for this domain ?
- private boolean assured = false;
- // Assured sub mode (used when assured is true)
- private AssuredMode assuredMode = AssuredMode.SAFE_DATA_MODE;
- // Safe Data level (used when assuredMode is SAFE_DATA)
- private byte assuredSdLevel = (byte)1;
- // Timeout (in milliseconds) when waiting for acknowledgments
- private long assuredTimeout = 1000;
-
- // Group id
- private byte groupId = (byte)1;
- // Referrals urls to be published to other servers of the topology
- // TODO: fill that with all currently opened urls if no urls configured
- private List<String> refUrls = new ArrayList<String>();
-
- // Current status for this replicated domain
- private ServerStatus status = ServerStatus.NOT_CONNECTED_STATUS;
-
- /*
- * Properties for the last topology info received from the network.
- */
- // Info for other DSs.
- // Warning: does not contain info for us (for our server id)
- private List<DSInfo> dsList = new ArrayList<DSInfo>();
- // Info for other RSs.
- private List<RSInfo> rsList = new ArrayList<RSInfo>();
+ // This list is used to temporary store operations that needs
+ // to be replayed at session establishment time.
+ private TreeSet<FakeOperation> replayOperations =
+ new TreeSet<FakeOperation>(new FakeOperationComparator());;
/**
* The isolation policy that this domain is going to use.
@@ -312,131 +244,47 @@
*/
private boolean done = true;
+ private ServerStateFlush flushThread;
+
/**
- * This class contain the context related to an import or export
- * launched on the domain.
+ * The thread that periodically saves the ServerState of this
+ * LDAPReplicationDomain in the database.
*/
- private class IEContext
+ private class ServerStateFlush extends DirectoryThread
{
- // The task that initiated the operation.
- Task initializeTask;
- // The input stream for the import
- ReplLDIFInputStream ldifImportInputStream = null;
- // The target in the case of an export
- short exportTarget = RoutableMsg.UNKNOWN_SERVER;
- // The source in the case of an import
- short importSource = RoutableMsg.UNKNOWN_SERVER;
-
- // The total entry count expected to be processed
- long entryCount = 0;
- // The count for the entry not yet processed
- long entryLeftCount = 0;
-
- // The exception raised when any
- DirectoryException exception = null;
-
- /**
- * Initializes the import/export counters with the provider value.
- * @param total
- * @param left
- * @throws DirectoryException
- */
- public void setCounters(long total, long left)
- throws DirectoryException
+ protected ServerStateFlush()
{
- entryCount = total;
- entryLeftCount = left;
-
- if (initializeTask != null)
- {
- if (initializeTask instanceof InitializeTask)
- {
- ((InitializeTask)initializeTask).setTotal(entryCount);
- ((InitializeTask)initializeTask).setLeft(entryCount);
- }
- else if (initializeTask instanceof InitializeTargetTask)
- {
- ((InitializeTargetTask)initializeTask).setTotal(entryCount);
- ((InitializeTargetTask)initializeTask).setLeft(entryCount);
- }
- }
- }
-
- /**
- * Update the counters of the task for each entry processed during
- * an import or export.
- * @throws DirectoryException
- */
- public void updateCounters()
- throws DirectoryException
- {
- entryLeftCount--;
-
- if (initializeTask != null)
- {
- if (initializeTask instanceof InitializeTask)
- {
- ((InitializeTask)initializeTask).setLeft(entryLeftCount);
- }
- else if (initializeTask instanceof InitializeTargetTask)
- {
- ((InitializeTargetTask)initializeTask).setLeft(entryLeftCount);
- }
- }
+ super("Replication State Saver for server id " +
+ serverId + " and domain " + baseDn.toString());
}
/**
* {@inheritDoc}
*/
- public String toString()
- {
- return new String("[ Entry count=" + this.entryCount +
- ", Entry left count=" + this.entryLeftCount + "]");
- }
- }
-
- /**
- * This thread is launched when we want to export data to another server that
- * has requested to be initialized with the data of our backend.
- */
- private class ExportThread extends DirectoryThread
- {
- // Id of server that will receive updates
- private short target;
-
- /**
- * Constructor for the ExportThread.
- *
- * @param target Id of server that will receive updates
- */
- public ExportThread(short target)
- {
- super("Export thread " + serverId);
- this.target = target;
- }
-
- /**
- * Run method for this class.
- */
+ @Override
public void run()
{
- if (debugEnabled())
- {
- TRACER.debugInfo("Export thread starting.");
- }
+ done = false;
- try
+ while (shutdown == false)
{
- initializeRemote(target, target, null);
- } catch (DirectoryException de)
- {
- // An error message has been sent to the peer
- // Nothing more to do locally
+ try
+ {
+ synchronized (this)
+ {
+ this.wait(1000);
+ if (!disabled && !stateSavingDisabled )
+ {
+ // save the ServerState
+ state.save();
+ }
+ }
+ } catch (InterruptedException e)
+ { }
}
- if (debugEnabled())
- {
- TRACER.debugInfo("Export thread stopping.");
- }
+ state.save();
+
+ done = true;
}
}
@@ -447,18 +295,24 @@
* @param updateToReplayQueue The queue for update messages to replay.
* @throws ConfigException In case of invalid configuration.
*/
- public ReplicationDomain(ReplicationDomainCfg configuration,
+ public LDAPReplicationDomain(ReplicationDomainCfg configuration,
LinkedBlockingQueue<UpdateToReplay> updateToReplayQueue)
throws ConfigException
{
- super("Replication State Saver for server id " + configuration.getServerId()
- + " and domain " + configuration.getBaseDN());
+ super(configuration.getBaseDN().toNormalizedString(),
+ (short) configuration.getServerId());
+
+ /**
+ * The time in milliseconds between heartbeats from the replication
+ * server. Zero means heartbeats are off.
+ */
+ long heartbeatInterval = 0;
// Read the configuration parameters.
- replicationServers = configuration.getReplicationServer();
+ Set<String> replicationServers = configuration.getReplicationServer();
serverId = (short) configuration.getServerId();
baseDn = configuration.getBaseDN();
- window = configuration.getWindowSize();
+ int window = configuration.getWindowSize();
heartbeatInterval = configuration.getHeartbeatInterval();
isolationpolicy = configuration.getIsolationPolicy();
configDn = configuration.dn();
@@ -471,28 +325,22 @@
switch (assuredType)
{
case NOT_ASSURED:
- assured = false;
+ setAssured(false);
break;
case SAFE_DATA:
- assured = true;
- this.assuredMode = AssuredMode.SAFE_DATA_MODE;
+ setAssured(true);
+ setAssuredMode(AssuredMode.SAFE_DATA_MODE);
break;
case SAFE_READ:
- assured = true;
- this.assuredMode = AssuredMode.SAFE_READ_MODE;
+ setAssured(true);
+ setAssuredMode(AssuredMode.SAFE_READ_MODE);
break;
}
- this.assuredSdLevel = (byte)configuration.getAssuredSdLevel();
- this.groupId = (byte)configuration.getGroupId();
- this.assuredTimeout = configuration.getAssuredTimeout();
- SortedSet<String> urls = configuration.getReferralsUrl();
- if (urls != null)
- {
- for (String url : urls)
- {
- this.refUrls.add(url);
- }
- }
+ setAssuredSdLevel((byte)configuration.getAssuredSdLevel());
+ setAssuredTimeout(configuration.getAssuredTimeout());
+
+ setGroupId((byte)configuration.getGroupId());
+ setURLs(configuration.getReferralsUrl());
/*
* Modify conflicts are solved for all suffixes but the schema suffix
@@ -510,19 +358,6 @@
solveConflictFlag = true;
}
- /*
- * Create a new Persistent Server State that will be used to store
- * the last ChangeNmber seen from all LDAP servers in the topology.
- */
- state = new PersistentServerState(baseDn, serverId);
-
- /*
- * Create a replication monitor object responsible for publishing
- * monitoring information below cn=monitor.
- */
- monitor = new ReplicationMonitor(this);
- DirectoryServer.registerMonitorProvider(monitor);
-
Backend backend = retrievesBackend(baseDn);
if (backend == null)
{
@@ -541,14 +376,12 @@
}
/*
- * create the broker object used to publish and receive changes
+ * Create a new Persistent Server State that will be used to store
+ * the last ChangeNmber seen from all LDAP servers in the topology.
*/
- broker = new ReplicationBroker(this, state, baseDn, serverId,
- maxReceiveQueue, maxReceiveDelay, maxSendQueue, maxSendDelay, window,
- heartbeatInterval, generationId,
- new ReplSessionSecurity(configuration),getGroupId());
+ state = new PersistentServerState(baseDn, serverId, getServerState());
- broker.start(replicationServers);
+ startPublishService(replicationServers, window, heartbeatInterval);
/*
* ChangeNumberGenerator is used to create new unique ChangeNumbers
@@ -557,14 +390,12 @@
* The generator time is adjusted to the time of the last CN received from
* remote other servers.
*/
- generator =
- new ChangeNumberGenerator(serverId, state);
+ generator = getGenerator();
pendingChanges =
- new PendingChanges(generator,
- broker, state);
+ new PendingChanges(generator, this);
- remotePendingChanges = new RemotePendingChanges(generator, state);
+ remotePendingChanges = new RemotePendingChanges(getServerState());
// listen for changes on the configuration
configuration.addChangeListener(this);
@@ -573,7 +404,6 @@
DirectoryServer.registerAlertGenerator(this);
}
-
/**
* Returns the base DN of this ReplicationDomain.
*
@@ -741,7 +571,7 @@
{
// this isolation policy specifies that the updates are denied
// when the broker is not connected.
- return broker.isConnected();
+ return isConnected();
}
// we should never get there as the only possible policies are
// ACCEPT_ALL_UPDATES and REJECT_ALL_UPDATES
@@ -931,322 +761,6 @@
}
/**
- * Receives an update message from the replicationServer.
- * also responsible for updating the list of pending changes
- * @return the received message - null if none
- */
- public UpdateMsg receive()
- {
- UpdateMsg update = null;
-
- while ( (update == null) && (!shutdown) )
- {
- InitializeRequestMsg initMsg = null;
- ReplicationMsg msg;
- try
- {
- msg = broker.receive();
- if (msg == null)
- {
- // The server is in the shutdown process
- return null;
- }
-
- if (debugEnabled())
- if (!(msg instanceof HeartbeatMsg))
- TRACER.debugVerbose("Message received <" + msg + ">");
-
- if (msg instanceof AckMsg)
- {
- AckMsg ack = (AckMsg) msg;
- receiveAck(ack);
- }
- else if (msg instanceof InitializeRequestMsg)
- {
- // Another server requests us to provide entries
- // for a total update
- initMsg = (InitializeRequestMsg)msg;
- }
- else if (msg instanceof InitializeTargetMsg)
- {
- // Another server is exporting its entries to us
- InitializeTargetMsg importMsg = (InitializeTargetMsg) msg;
-
- try
- {
- // This must be done while we are still holding the
- // broker lock because we are now going to receive a
- // bunch of entries from the remote server and we
- // want the import thread to catch them and
- // not the ListenerThread.
- initialize(importMsg);
- }
- catch(DirectoryException de)
- {
- // Returns an error message to notify the sender
- ErrorMsg errorMsg =
- new ErrorMsg(importMsg.getsenderID(),
- de.getMessageObject());
- MessageBuilder mb = new MessageBuilder();
- mb.append(de.getMessageObject());
- TRACER.debugInfo(Message.toString(mb.toMessage()));
- broker.publish(errorMsg);
- }
- }
- else if (msg instanceof ErrorMsg)
- {
- if (ieContext != null)
- {
- // This is an error termination for the 2 following cases :
- // - either during an export
- // - or before an import really started
- // For example, when we publish a request and the
- // replicationServer did not find any import source.
- abandonImportExport((ErrorMsg)msg);
- }
- else
- {
- /*
- * Log error message
- */
- ErrorMsg errorMsg = (ErrorMsg)msg;
- logError(ERR_ERROR_MSG_RECEIVED.get(
- errorMsg.getDetails()));
- }
- }
- if (msg instanceof TopologyMsg)
- {
- TopologyMsg topoMsg = (TopologyMsg)msg;
- receiveTopo(topoMsg);
- }
- if (msg instanceof ChangeStatusMsg)
- {
- ChangeStatusMsg csMsg = (ChangeStatusMsg)msg;
- receiveChangeStatus(csMsg);
- }
- else if (msg instanceof UpdateMsg)
- {
- update = (UpdateMsg) msg;
- receiveUpdate(update);
- }
- }
- catch (SocketTimeoutException e)
- {
- // just retry
- }
- // Test if we have received and export request message and
- // if that's the case handle it now.
- // This must be done outside of the portion of code protected
- // by the broker lock so that we keep receiveing update
- // when we are doing and export and so that a possible
- // closure of the socket happening when we are publishing the
- // entries to the remote can be handled by the other
- // replay thread when they call this method and therefore the
- // broker.receive() method.
- if (initMsg != null)
- {
- // Do this work in a thread to allow replay thread continue working
- ExportThread exportThread = new ExportThread(initMsg.getsenderID());
- exportThread.start();
- }
- }
- return update;
- }
-
- /**
- * Processes an incoming TopologyMsg.
- * Updates the structures for the local view of the topology.
- *
- * @param topoMsg The topology information received from RS.
- */
- public void receiveTopo(TopologyMsg topoMsg)
- {
-
- if (debugEnabled())
- TRACER.debugInfo("Replication domain " + baseDn
- + " received topology info update:\n" + topoMsg);
-
- // Store new lists
- synchronized(getDsList())
- {
- synchronized(getRsList())
- {
- dsList = topoMsg.getDsList();
- rsList = topoMsg.getRsList();
- }
- }
- }
-
- /**
- * Set the initial status of the domain, once he is connected to the topology.
- * @param initStatus The status to enter the state machine with
- */
- public void setInitialStatus(ServerStatus initStatus)
- {
- // Sanity check: is it a valid initial status?
- if (!isValidInitialStatus(initStatus))
- {
- Message msg = ERR_DS_INVALID_INIT_STATUS.get(initStatus.toString(),
- baseDn.toString(), Short.toString(serverId));
- logError(msg);
- } else
- {
- status = initStatus;
- }
- }
-
- /**
- * Processes an incoming ChangeStatusMsg. Compute new status according to
- * given order. Then update domain for being compliant with new status
- * definition.
- * @param csMsg The received status message
- */
- private void receiveChangeStatus(ChangeStatusMsg csMsg)
- {
- if (debugEnabled())
- TRACER.debugInfo("Replication domain " + baseDn +
- " received change status message:\n" + csMsg);
-
- ServerStatus reqStatus = csMsg.getRequestedStatus();
-
- // Translate requested status to a state machine event
- StatusMachineEvent event = StatusMachineEvent.statusToEvent(reqStatus);
- if (event == StatusMachineEvent.INVALID_EVENT)
- {
- Message msg = ERR_DS_INVALID_REQUESTED_STATUS.get(reqStatus.toString(),
- baseDn.toString(), Short.toString(serverId));
- logError(msg);
- return;
- }
-
- // Compute new status and do matching tasks
- // Use synchronized as admin task (thread) could order to go in admin status
- // for instance (concurrent with receive thread).
- synchronized (status)
- {
- ServerStatus newStatus =
- StatusMachine.computeNewStatus(status, event);
-
- if (newStatus == ServerStatus.INVALID_STATUS)
- {
- Message msg = ERR_DS_CANNOT_CHANGE_STATUS.get(baseDn.toString(),
- Short.toString(serverId), status.toString(), event.toString());
- logError(msg);
- return;
- }
-
- // Store new status
- status = newStatus;
-
- if (debugEnabled())
- TRACER.debugInfo("Replication domain " + baseDn +
- " new status is: " + status);
-
- // Perform whatever actions are needed to apply properties for being
- // compliant with new status
- updateDomainForNewStatus();
- }
- }
-
- /**
- * Called when first connection or disconnection detected.
- */
- public void toNotConnectedStatus()
- {
- // Go into not connected status
- // Use synchronized as somebody could ask another status change at the same
- // time
- synchronized (status)
- {
- StatusMachineEvent event =
- StatusMachineEvent.TO_NOT_CONNECTED_STATUS_EVENT;
- ServerStatus newStatus =
- StatusMachine.computeNewStatus(status, event);
-
- if (newStatus == ServerStatus.INVALID_STATUS)
- {
- Message msg = ERR_DS_CANNOT_CHANGE_STATUS.get(baseDn.toString(),
- Short.toString(serverId), status.toString(), event.toString());
- logError(msg);
- return;
- }
-
- // Store new status
- status = newStatus;
-
- if (debugEnabled())
- TRACER.debugInfo("Replication domain " + baseDn +
- " new status is: " + status);
-
- // Perform whatever actions are needed to apply properties for being
- // compliant with new status
- updateDomainForNewStatus();
- }
- }
-
- /**
- * Perform whatever actions are needed to apply properties for being
- * compliant with new status. Must be called in synchronized section for
- * status. The new status is already set in status variable.
- */
- private void updateDomainForNewStatus()
- {
- switch (status)
- {
- case NOT_CONNECTED_STATUS:
- break;
- case NORMAL_STATUS:
- break;
- case DEGRADED_STATUS:
- break;
- case FULL_UPDATE_STATUS:
- // Signal RS we just entered the full update status
- broker.signalStatusChange(status);
- break;
- case BAD_GEN_ID_STATUS:
- break;
- default:
- if (debugEnabled())
- TRACER.debugInfo("updateDomainForNewStatus: unexpected status: " +
- status);
- }
- }
-
- /**
- * Do the necessary processing when an UpdateMsg was received.
- *
- * @param update The received UpdateMsg.
- */
- public void receiveUpdate(UpdateMsg update)
- {
- remotePendingChanges.putRemoteUpdate(update);
- numRcvdUpdates.incrementAndGet();
- }
-
- /**
- * Do the necessary processing when an AckMsg is received.
- *
- * @param ack The AckMsg that was received.
- */
- public void receiveAck(AckMsg ack)
- {
- UpdateMsg update;
- ChangeNumber changeNumber = ack.getChangeNumber();
-
- synchronized (waitingAckMsgs)
- {
- update = waitingAckMsgs.remove(changeNumber);
- }
- if (update != null)
- {
- synchronized (update)
- {
- update.notify();
- }
- }
- }
-
- /**
* Check if an operation must be synchronized.
* Also update the list of pending changes and the server RUV
* @param op the operation
@@ -1258,19 +772,17 @@
{
numReplayedPostOpCalled++;
}
- UpdateMsg msg = null;
+ LDAPUpdateMsg msg = null;
// Note that a failed non-replication operation might not have a change
// number.
ChangeNumber curChangeNumber = OperationContext.getChangeNumber(op);
- boolean isAssured = isAssured(op);
-
if ((result == ResultCode.SUCCESS) && (!op.isSynchronizationOperation()))
{
// Generate a replication message for a successful non-replication
// operation.
- msg = UpdateMsg.generateMsg(op);
+ msg = LDAPUpdateMsg.generateMsg(op);
if (msg == null)
{
@@ -1307,16 +819,6 @@
return;
}
- if (msg != null && isAssured)
- {
- synchronized (waitingAckMsgs)
- {
- // Add the assured message to the list of update that are
- // waiting acknowledgements
- waitingAckMsgs.put(curChangeNumber, msg);
- }
- }
-
if (generationIdSavedStatus != true)
{
this.saveGenerationId(generationId);
@@ -1334,53 +836,8 @@
if (!op.isSynchronizationOperation())
{
- int pushedChanges = pendingChanges.pushCommittedChanges();
- numSentUpdates.addAndGet(pushedChanges);
+ pendingChanges.pushCommittedChanges();
}
-
- // Wait for acknowledgement of an assured message.
- if (msg != null && isAssured)
- {
- synchronized (msg)
- {
- while (waitingAckMsgs.containsKey(msg.getChangeNumber()))
- {
- // TODO : should have a configurable timeout to get
- // out of this loop
- try
- {
- msg.wait(1000);
- } catch (InterruptedException e)
- { }
- }
- }
- }
- }
-
- /**
- * get the number of updates received by the replication plugin.
- *
- * @return the number of updates received
- */
- public int getNumRcvdUpdates()
- {
- if (numRcvdUpdates != null)
- return numRcvdUpdates.get();
- else
- return 0;
- }
-
- /**
- * Get the number of updates sent by the replication plugin.
- *
- * @return the number of updates sent
- */
- public int getNumSentUpdates()
- {
- if (numSentUpdates != null)
- return numSentUpdates.get();
- else
- return 0;
}
/**
@@ -1397,27 +854,6 @@
}
/**
- * Increment the number of processed updates.
- */
- public void incProcessedUpdates()
- {
- numProcessedUpdates.incrementAndGet();
- }
-
- /**
- * get the number of updates replayed by the replication.
- *
- * @return The number of updates replayed by the replication
- */
- public int getNumProcessedUpdates()
- {
- if (numProcessedUpdates != null)
- return numProcessedUpdates.get();
- else
- return 0;
- }
-
- /**
* get the number of updates replayed successfully by the replication.
*
* @return The number of updates replayed successfully
@@ -1428,16 +864,6 @@
}
/**
- * get the ServerState.
- *
- * @return the ServerState
- */
- public ServerState getServerState()
- {
- return state;
- }
-
- /**
* Get the debugCount.
*
* @return Returns the debugCount.
@@ -1448,49 +874,6 @@
}
/**
- * Send an Ack message.
- *
- * @param changeNumber The ChangeNumber for which the ack must be sent.
- */
- public void ack(ChangeNumber changeNumber)
- {
- broker.publish(new AckMsg(changeNumber));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void run()
- {
- done = false;
-
- // Create the listener thread
- listenerThread = new ListenerThread(this, updateToReplayQueue);
- listenerThread.start();
-
- while (shutdown == false)
- {
- try
- {
- synchronized (this)
- {
- this.wait(1000);
- if (!disabled && !stateSavingDisabled )
- {
- // save the RUV
- state.save();
- }
- }
- } catch (InterruptedException e)
- { }
- }
- state.save();
-
- done = true;
- }
-
- /**
* Shutdown this ReplicationDomain.
*/
public void shutdown()
@@ -1498,27 +881,19 @@
// stop the flush thread
shutdown = true;
- // Stop the listener thread
- if (listenerThread != null)
+ // stop the thread in charge of flushing the ServerState.
+ if (flushThread != null)
{
- listenerThread.shutdown();
+ synchronized (flushThread)
+ {
+ flushThread.notify();
+ }
}
- synchronized (this)
- {
- this.notify();
- }
-
- DirectoryServer.deregisterMonitorProvider(monitor.getMonitorInstanceName());
-
DirectoryServer.deregisterAlertGenerator(this);
- // stop the ReplicationBroker
- broker.stop();
-
- // Wait for the listener thread to stop
- if (listenerThread != null)
- listenerThread.waitForShutdown();
+ // stop the ReplicationDomain
+ stopDomain();
// wait for completion of the persistentServerState thread.
try
@@ -1534,26 +909,11 @@
}
/**
- * Get the name of the replicationServer to which this domain is currently
- * connected.
- *
- * @return the name of the replicationServer to which this domain
- * is currently connected.
- */
- public String getReplicationServer()
- {
- if (broker != null)
- return broker.getReplicationServer();
- else
- return "Not connected";
- }
-
- /**
* Create and replay a synchronized Operation from an UpdateMsg.
*
* @param msg The UpdateMsg to be replayed.
*/
- public void replay(UpdateMsg msg)
+ public void replay(LDAPUpdateMsg msg)
{
Operation op = null;
boolean done = false;
@@ -1565,6 +925,7 @@
// whose dependency has been replayed until no more left.
do
{
+ String replayErrorMsg = null;
try
{
op = msg.createOperation(conn);
@@ -1572,12 +933,12 @@
while ((!dependency) && (!done) && (retryCount-- > 0))
{
+ // Try replay the operation
op.setInternalOperation(true);
op.setSynchronizationOperation(true);
changeNumber = OperationContext.getChangeNumber(op);
((AbstractOperation) op).run();
- // Try replay the operation
ResultCode result = op.getResultCode();
if (result != ResultCode.SUCCESS)
@@ -1638,7 +999,7 @@
op.getErrorMessage().toString());
logError(message);
numUnresolvedNamingConflicts.incrementAndGet();
-
+ replayErrorMsg = message.toString();
updateError(changeNumber);
}
} catch (ASN1Exception e)
@@ -1646,16 +1007,19 @@
Message message = ERR_EXCEPTION_DECODING_OPERATION.get(
String.valueOf(msg) + stackTraceToSingleLineString(e));
logError(message);
+ replayErrorMsg = message.toString();
} catch (LDAPException e)
{
Message message = ERR_EXCEPTION_DECODING_OPERATION.get(
String.valueOf(msg) + stackTraceToSingleLineString(e));
logError(message);
+ replayErrorMsg = message.toString();
} catch (DataFormatException e)
{
Message message = ERR_EXCEPTION_DECODING_OPERATION.get(
String.valueOf(msg) + stackTraceToSingleLineString(e));
logError(message);
+ replayErrorMsg = message.toString();
} catch (Exception e)
{
if (changeNumber != null)
@@ -1669,21 +1033,20 @@
Message message = ERR_EXCEPTION_REPLAYING_OPERATION.get(
stackTraceToSingleLineString(e), op.toString());
logError(message);
+ replayErrorMsg = message.toString();
updateError(changeNumber);
} else
{
Message message = ERR_EXCEPTION_DECODING_OPERATION.get(
String.valueOf(msg) + stackTraceToSingleLineString(e));
logError(message);
+ replayErrorMsg = message.toString();
}
} finally
{
if (!dependency)
{
- broker.updateWindowAfterReplay();
- if (msg.isAssured())
- ack(msg.getChangeNumber());
- incProcessedUpdates();
+ processUpdateDone(msg, replayErrorMsg);
}
}
@@ -1913,7 +1276,7 @@
* @return true if the process is completed, false if it must continue..
*/
private boolean solveNamingConflict(DeleteOperation op,
- UpdateMsg msg)
+ LDAPUpdateMsg msg)
{
ResultCode result = op.getResultCode();
DeleteContext ctx = (DeleteContext) op.getAttachment(SYNCHROCONTEXT);
@@ -1983,7 +1346,7 @@
* @throws Exception When the operation is not valid.
*/
private boolean solveNamingConflict(ModifyDNOperation op,
- UpdateMsg msg) throws Exception
+ LDAPUpdateMsg msg) throws Exception
{
ResultCode result = op.getResultCode();
ModifyDnContext ctx = (ModifyDnContext) op.getAttachment(SYNCHROCONTEXT);
@@ -2391,83 +1754,6 @@
}
/**
- * Check if an operation must be processed as an assured operation.
- *
- * @param op the operation to be checked.
- * @return true if the operations must be processed as an assured operation.
- */
- private boolean isAssured(PostOperationOperation op)
- {
- // TODO : should have a filtering mechanism for checking
- // operation that are assured and operations that are not.
- return false;
- }
-
- /**
- * Get the maximum receive window size.
- *
- * @return The maximum receive window size.
- */
- public int getMaxRcvWindow()
- {
- if (broker != null)
- return broker.getMaxRcvWindow();
- else
- return 0;
- }
-
- /**
- * Get the current receive window size.
- *
- * @return The current receive window size.
- */
- public int getCurrentRcvWindow()
- {
- if (broker != null)
- return broker.getCurrentRcvWindow();
- else
- return 0;
- }
-
- /**
- * Get the maximum send window size.
- *
- * @return The maximum send window size.
- */
- public int getMaxSendWindow()
- {
- if (broker != null)
- return broker.getMaxSendWindow();
- else
- return 0;
- }
-
- /**
- * Get the current send window size.
- *
- * @return The current send window size.
- */
- public int getCurrentSendWindow()
- {
- if (broker != null)
- return broker.getCurrentSendWindow();
- else
- return 0;
- }
-
- /**
- * Get the number of times the replication connection was lost.
- * @return The number of times the replication connection was lost.
- */
- public int getNumLostConnections()
- {
- if (broker != null)
- return broker.getNumLostConnections();
- else
- return 0;
- }
-
- /**
* Get the number of modify conflicts successfully resolved.
* @return The number of modify conflicts successfully resolved.
*/
@@ -2477,7 +1763,7 @@
}
/**
- * Get the number of namign conflicts successfully resolved.
+ * Get the number of naming conflicts successfully resolved.
* @return The number of naming conflicts successfully resolved.
*/
public int getNumResolvedNamingConflicts()
@@ -2495,18 +1781,9 @@
}
/**
- * Get the server ID.
- * @return The server ID.
- */
- public short getServerId()
- {
- return serverId;
- }
-
- /**
* Check if the domain solve conflicts.
*
- * @return a boolean indicating if the domain should sove conflicts.
+ * @return a boolean indicating if the domain should solve conflicts.
*/
public boolean solveConflict()
{
@@ -2526,16 +1803,7 @@
state.save();
state.clearInMemory();
disabled = true;
-
- // Stop the listener thread
- if (listenerThread != null)
- listenerThread.shutdown();
-
- broker.stop(); // This will cut the session and wake up the listener
-
- // Wait for the listener thread to stop
- if (listenerThread != null)
- listenerThread.waitForShutdown();
+ disableService(); // This will cut the session and wake up the listener
}
/**
@@ -2578,16 +1846,7 @@
return;
}
- // After an on-line import, the value of the generationId is new
- // and it is necessary for the broker to send this new value as part
- // of the serverStart message.
- broker.setGenerationId(generationId);
-
- broker.start(replicationServers);
-
- // Create the listener thread
- listenerThread = new ListenerThread(this, updateToReplayQueue);
- listenerThread.start();
+ enableService();
disabled = false;
}
@@ -2600,7 +1859,7 @@
*/
public long computeGenerationId() throws DirectoryException
{
- long genId = exportBackend(true);
+ long genId = exportBackend(null, true);
if (debugEnabled())
TRACER.debugInfo("Computed generationId: generationId=" + genId);
@@ -2609,11 +1868,9 @@
}
/**
- * Returns the generationId set for this domain.
- *
- * @return The generationId.
+ * {@inheritDoc}
*/
- public long getGenerationId()
+ public long getGenerationID()
{
return generationId;
}
@@ -2627,7 +1884,7 @@
/**
* Stores the value of the generationId.
* @param generationId The value of the generationId.
- * @return a ResultCode indicating if the method was successfull.
+ * @return a ResultCode indicating if the method was successful.
*/
public ResultCode saveGenerationId(long generationId)
{
@@ -2792,45 +2049,8 @@
}
/**
- * Reset the generationId of this domain in the whole topology.
- * A message is sent to the Replication Servers for them to reset
- * their change dbs.
- *
- * @param generationIdNewValue The new value of the generation Id.
- * @throws DirectoryException when an error occurs
- */
- public void resetGenerationId(Long generationIdNewValue)
- throws DirectoryException
- {
- if (debugEnabled())
- TRACER.debugInfo(
- this.getName() + "resetGenerationId" + generationIdNewValue);
-
- if (!isConnected())
- {
- ResultCode resultCode = ResultCode.OTHER;
- Message message = ERR_RESET_GENERATION_CONN_ERR_ID.get(
- baseDn.toNormalizedString());
- throw new DirectoryException(
- resultCode, message);
- }
-
- ResetGenerationIdMsg genIdMessage = null;
-
- if (generationIdNewValue == null)
- {
- genIdMessage = new ResetGenerationIdMsg(this.generationId);
- }
- else
- {
- genIdMessage = new ResetGenerationIdMsg(generationIdNewValue);
- }
- broker.publish(genIdMessage);
- }
-
- /**
* Do whatever is needed when a backup is started.
- * We need to make sure that the serverState is correclty save.
+ * We need to make sure that the serverState is correctly save.
*/
public void backupStart()
{
@@ -2849,103 +2069,7 @@
* Total Update >>
*/
- /**
- * Receives bytes related to an entry in the context of an import to
- * initialize the domain (called by ReplLDIFInputStream).
- *
- * @return The bytes. Null when the Done or Err message has been received
- */
- public byte[] receiveEntryBytes()
- {
- ReplicationMsg msg;
- while (true)
- {
- try
- {
- msg = broker.receive();
- if (debugEnabled())
- TRACER.debugVerbose(
- " sid:" + this.serverId +
- " base DN:" + this.baseDn +
- " Import EntryBytes received " + msg);
- if (msg == null)
- {
- // The server is in the shutdown process
- return null;
- }
-
- if (msg instanceof EntryMsg)
- {
- EntryMsg entryMsg = (EntryMsg)msg;
- byte[] entryBytes = entryMsg.getEntryBytes();
- ieContext.updateCounters();
- return entryBytes;
- }
- else if (msg instanceof DoneMsg)
- {
- // This is the normal termination of the import
- // No error is stored and the import is ended
- // by returning null
- return null;
- }
- else if (msg instanceof ErrorMsg)
- {
- // This is an error termination during the import
- // The error is stored and the import is ended
- // by returning null
- ErrorMsg errorMsg = (ErrorMsg)msg;
- ieContext.exception = new DirectoryException(
- ResultCode.OTHER,
- errorMsg.getDetails());
- return null;
- }
- else
- {
- // Other messages received during an import are trashed
- }
- }
- catch(Exception e)
- {
- // TODO: i18n
- ieContext.exception = new DirectoryException(ResultCode.OTHER,
- Message.raw("received an unexpected message type" +
- e.getLocalizedMessage()));
- }
- }
- }
-
- /**
- * Processes an error message received while an import/export is
- * on going.
- * @param errorMsg The error message received.
- */
- protected void abandonImportExport(ErrorMsg errorMsg)
- {
- // FIXME TBD Treat the case where the error happens while entries
- // are being exported
-
- if (debugEnabled())
- TRACER.debugVerbose(
- " abandonImportExport:" + this.serverId +
- " base DN:" + this.baseDn +
- " Error Msg received " + errorMsg);
-
- if (ieContext != null)
- {
- ieContext.exception = new DirectoryException(ResultCode.OTHER,
- errorMsg.getDetails());
-
- if (ieContext.initializeTask instanceof InitializeTask)
- {
- // Update the task that initiated the import
- ((InitializeTask)ieContext.initializeTask).
- updateTaskCompletionState(ieContext.exception);
-
- releaseIEContext();
- }
- }
- }
/**
* Clears all the entries from the JE backend determined by the
@@ -3000,16 +2124,31 @@
}
/**
+ * This method trigger an export of the replicated data.
+ *
+ * @param output The OutputStream where the export should
+ * be produced.
+ * @throws DirectoryException When needed.
+ */
+ protected void exportBackend(OutputStream output) throws DirectoryException
+ {
+ exportBackend(output, false);
+ }
+
+ /**
* Export the entries from the backend and/or compute the generation ID.
* The ieContext must have been set before calling.
- * @param checksumOutput true is the exportBackend is called to compute
- * the generationID
*
- * @return The computed generationID.
+ * @param output The OutputStream where the export should
+ * be produced.
+ * @param checksumOutput A boolean indicating if this export is
+ * invoked to perform a checksum only
+ *
+ * @return The computed GenerationID.
*
* @throws DirectoryException when an error occurred
*/
- protected long exportBackend(boolean checksumOutput)
+ protected long exportBackend(OutputStream output, boolean checksumOutput)
throws DirectoryException
{
long genID = 0;
@@ -3042,7 +2181,7 @@
}
OutputStream os;
- ReplLDIFOutputStream ros;
+ ReplLDIFOutputStream ros = null;
if (checksumOutput)
{
@@ -3060,8 +2199,7 @@
}
else
{
- ros = new ReplLDIFOutputStream(this, (short)-1);
- os = ros;
+ os = output;
}
LDIFExportConfig exportConfig = new LDIFExportConfig(os);
@@ -3096,7 +2234,7 @@
}
catch (DirectoryException de)
{
- if ((checksumOutput) &&
+ if ((ros != null) &&
(ros.getNumExportedEntries() >= entryCount))
{
// This is the normal end when computing the generationId
@@ -3170,277 +2308,6 @@
}
/**
- * Get the internal broker to perform some operations on it.
- *
- * @return The broker for this domain.
- */
- ReplicationBroker getBroker()
- {
- return broker;
- }
-
- /**
- * Exports an entry in LDIF format.
- *
- * @param lDIFEntry The entry to be exported..
- *
- * @throws IOException when an error occurred.
- */
- public void exportLDIFEntry(String lDIFEntry) throws IOException
- {
- // If an error was raised - like receiving an ErrorMsg
- // we just let down the export.
- if (ieContext.exception != null)
- {
- IOException ioe = new IOException(ieContext.exception.getMessage());
- ieContext = null;
- throw ioe;
- }
-
- EntryMsg entryMessage = new EntryMsg(
- serverId, ieContext.exportTarget, lDIFEntry.getBytes());
- broker.publish(entryMessage);
-
- try
- {
- ieContext.updateCounters();
- }
- catch (DirectoryException de)
- {
- throw new IOException(de.getMessage());
- }
- }
-
- /**
- * Initializes this domain from another source server.
- *
- * @param source The source from which to initialize
- * @param initTask The task that launched the initialization
- * and should be updated of its progress.
- * @throws DirectoryException when an error occurs
- */
- public void initializeFromRemote(short source, Task initTask)
- throws DirectoryException
- {
- if (debugEnabled())
- TRACER.debugInfo("Entering initializeFromRemote");
-
- acquireIEContext();
- ieContext.initializeTask = initTask;
-
- InitializeRequestMsg initializeMsg = new InitializeRequestMsg(
- baseDn, serverId, source);
-
- // Publish Init request msg
- broker.publish(initializeMsg);
-
- // .. we expect to receive entries or err after that
- }
-
- /**
- * Verifies that the given string represents a valid source
- * from which this server can be initialized.
- * @param sourceString The string representing the source
- * @return The source as a short value
- * @throws DirectoryException if the string is not valid
- */
- public short decodeSource(String sourceString)
- throws DirectoryException
- {
- short source = 0;
- Throwable cause = null;
- try
- {
- source = Integer.decode(sourceString).shortValue();
- if ((source >= -1) && (source != serverId))
- {
- // TODO Verifies serverID is in the domain
- // We shold check here that this is a server implied
- // in the current domain.
- return source;
- }
- }
- catch(Exception e)
- {
- cause = e;
- }
-
- ResultCode resultCode = ResultCode.OTHER;
- Message message = ERR_INVALID_IMPORT_SOURCE.get();
- if (cause != null)
- {
- throw new DirectoryException(
- resultCode, message, cause);
- }
- else
- {
- throw new DirectoryException(
- resultCode, message);
- }
- }
-
- /**
- * Verifies that the given string represents a valid source
- * from which this server can be initialized.
- * @param targetString The string representing the source
- * @return The source as a short value
- * @throws DirectoryException if the string is not valid
- */
- public short decodeTarget(String targetString)
- throws DirectoryException
- {
- short target = 0;
- Throwable cause;
- if (targetString.equalsIgnoreCase("all"))
- {
- return RoutableMsg.ALL_SERVERS;
- }
-
- // So should be a serverID
- try
- {
- target = Integer.decode(targetString).shortValue();
- if (target >= 0)
- {
- // FIXME Could we check now that it is a know server in the domain ?
- }
- return target;
- }
- catch(Exception e)
- {
- cause = e;
- }
- ResultCode resultCode = ResultCode.OTHER;
- Message message = ERR_INVALID_EXPORT_TARGET.get();
-
- if (cause != null)
- throw new DirectoryException(
- resultCode, message, cause);
- else
- throw new DirectoryException(
- resultCode, message);
-
- }
-
- private synchronized void acquireIEContext()
- throws DirectoryException
- {
- if (ieContext != null)
- {
- // Rejects 2 simultaneous exports
- Message message = ERR_SIMULTANEOUS_IMPORT_EXPORT_REJECTED.get();
- throw new DirectoryException(ResultCode.OTHER,
- message);
- }
-
- ieContext = new IEContext();
- }
-
- private synchronized void releaseIEContext()
- {
- ieContext = null;
- }
-
- /**
- * Process the initialization of some other server or servers in the topology
- * specified by the target argument.
- * @param target The target that should be initialized
- * @param initTask The task that triggers this initialization and that should
- * be updated with its progress.
- *
- * @exception DirectoryException When an error occurs.
- */
- public void initializeRemote(short target, Task initTask)
- throws DirectoryException
- {
- initializeRemote(target, serverId, initTask);
- }
-
- /**
- * Process the initialization of some other server or servers in the topology
- * specified by the target argument when this initialization specifying the
- * server that requests the initialization.
- *
- * @param target The target that should be initialized.
- * @param requestorID The server that initiated the export.
- * @param initTask The task that triggers this initialization and that should
- * be updated with its progress.
- *
- * @exception DirectoryException When an error occurs.
- */
- public void initializeRemote(short target, short requestorID, Task initTask)
- throws DirectoryException
- {
- Message msg = NOTE_FULL_UPDATE_ENGAGED_FOR_REMOTE_START.get(
- Short.toString(serverId),
- baseDn.toString(),
- Short.toString(requestorID));
- logError(msg);
-
- boolean contextAcquired=false;
-
- try
- {
- Backend backend = retrievesBackend(this.baseDn);
-
- if (!backend.supportsLDIFExport())
- {
- Message message = ERR_INIT_EXPORT_NOT_SUPPORTED.get(
- backend.getBackendID().toString());
- logError(message);
- throw new DirectoryException(ResultCode.OTHER, message);
- }
-
- acquireIEContext();
- contextAcquired = true;
-
- // The number of entries to be exported is the number of entries under
- // the base DN entry and the base entry itself.
- long entryCount = backend.numSubordinates(baseDn, true) + 1;
- ieContext.exportTarget = target;
- if (initTask != null)
- {
- ieContext.initializeTask = initTask;
- }
- ieContext.setCounters(entryCount, entryCount);
-
- // Send start message to the peer
- InitializeTargetMsg initializeMessage = new InitializeTargetMsg(
- baseDn, serverId, ieContext.exportTarget, requestorID, entryCount);
-
- broker.publish(initializeMessage);
-
- exportBackend(false);
-
- // Notify the peer of the success
- DoneMsg doneMsg = new DoneMsg(serverId,
- initializeMessage.getDestination());
- broker.publish(doneMsg);
-
- releaseIEContext();
- }
- catch(DirectoryException de)
- {
- // Notify the peer of the failure
- ErrorMsg errorMsg =
- new ErrorMsg(target,
- de.getMessageObject());
- broker.publish(errorMsg);
-
- if (contextAcquired)
- releaseIEContext();
-
- throw(de);
- }
-
- msg = NOTE_FULL_UPDATE_ENGAGED_FOR_REMOTE_END.get(
- Short.toString(serverId),
- baseDn.toString(),
- Short.toString(requestorID));
- logError(msg);
- }
-
- /**
* Process backend before import.
* @param backend The backend.
* @throws Exception
@@ -3468,51 +2335,16 @@
}
/**
- * Initializes the domain's backend with received entries.
- * @param initializeMessage The message that initiated the import.
- * @exception DirectoryException Thrown when an error occurs.
+ * This method should trigger an import of the replicated data.
+ *
+ * @param input The InputStream from which
+ * @throws DirectoryException When needed.
*/
- protected void initialize(InitializeTargetMsg initializeMessage)
- throws DirectoryException
+ public void importBackend(InputStream input) throws DirectoryException
{
LDIFImportConfig importConfig = null;
DirectoryException de = null;
- Message msg = NOTE_FULL_UPDATE_ENGAGED_FROM_REMOTE_START.get(
- Short.toString(serverId),
- baseDn.toString(),
- Long.toString(initializeMessage.getRequestorID()));
- logError(msg);
-
- // Go into full update status
- // Use synchronized as somebody could ask another status change at the same
- // time
- synchronized (status)
- {
- StatusMachineEvent event = StatusMachineEvent.TO_FULL_UPDATE_STATUS_EVENT;
- ServerStatus newStatus =
- StatusMachine.computeNewStatus(status, event);
-
- if (newStatus == ServerStatus.INVALID_STATUS)
- {
- msg = ERR_DS_CANNOT_CHANGE_STATUS.get(baseDn.toString(),
- Short.toString(serverId), status.toString(), event.toString());
- logError(msg);
- return;
- }
-
- // Store new status
- status = newStatus;
-
- if (debugEnabled())
- TRACER.debugInfo("Replication domain " + baseDn +
- " new status is: " + status);
-
- // Perform whatever actions are needed to apply properties for being
- // compliant with new status
- updateDomainForNewStatus();
- }
-
Backend backend = retrievesBackend(baseDn);
try
@@ -3526,26 +2358,8 @@
}
else
{
- if (initializeMessage.getRequestorID() == serverId)
- {
- // The import responds to a request we did so the IEContext
- // is already acquired
- }
- else
- {
- acquireIEContext();
- }
-
- ieContext.importSource = initializeMessage.getsenderID();
- ieContext.entryLeftCount = initializeMessage.getEntryCount();
- ieContext.setCounters(initializeMessage.getEntryCount(),
- initializeMessage.getEntryCount());
-
- preBackendImport(backend);
-
- ieContext.ldifImportInputStream = new ReplLDIFInputStream(this);
importConfig =
- new LDIFImportConfig(ieContext.ldifImportInputStream);
+ new LDIFImportConfig(input);
List<DN> includeBranches = new ArrayList<DN>();
includeBranches.add(this.baseDn);
importConfig.setIncludeBranches(includeBranches);
@@ -3553,11 +2367,12 @@
// TODO How to deal with rejected entries during the import
importConfig.writeRejectedEntries(
- getFileForPath("logs" + File.separator +
- "replInitRejectedEntries").getAbsolutePath(),
- ExistingFileBehavior.OVERWRITE);
+ getFileForPath("logs" + File.separator +
+ "replInitRejectedEntries").getAbsolutePath(),
+ ExistingFileBehavior.OVERWRITE);
// Process import
+ preBackendImport(backend);
backend.importLDIF(importConfig);
stateSavingDisabled = false;
@@ -3570,9 +2385,6 @@
}
finally
{
- if ((ieContext != null) && (ieContext.exception != null))
- de = ieContext.exception;
-
// Cleanup
if (importConfig != null)
{
@@ -3592,7 +2404,6 @@
TRACER.debugInfo(
"After import, the replication plugin restarts connections" +
" to all RSs to provide new generation ID=" + generationId);
- broker.setGenerationId(generationId);
}
catch (DirectoryException fe)
{
@@ -3604,29 +2415,12 @@
if (de == null)
de = fe;
}
-
- // Re-exchange generationID and state with RS
- broker.reStart();
-
- // Update the task that initiated the import
- if ((ieContext != null ) && (ieContext.initializeTask != null))
- {
- ((InitializeTask)ieContext.initializeTask).
- updateTaskCompletionState(de);
- }
- releaseIEContext();
}
// Sends up the root error.
if (de != null)
{
throw de;
}
-
- msg = NOTE_FULL_UPDATE_ENGAGED_FROM_REMOTE_END.get(
- Short.toString(serverId),
- baseDn.toString(),
- Long.toString(initializeMessage.getRequestorID()));
- logError(msg);
}
/**
@@ -3660,10 +2454,10 @@
* @throws DirectoryException When an error occurred or no domain
* match the provided baseDn.
*/
- public static ReplicationDomain retrievesReplicationDomain(DN baseDn)
+ public static LDAPReplicationDomain retrievesReplicationDomain(DN baseDn)
throws DirectoryException
{
- ReplicationDomain replicationDomain = null;
+ LDAPReplicationDomain replicationDomain = null;
// Retrieves the domain
DirectoryServer.getSynchronizationProviders();
@@ -3678,7 +2472,7 @@
}
// From the domainDN retrieves the replication domain
- ReplicationDomain sdomain =
+ LDAPReplicationDomain sdomain =
MultimasterReplication.findDomain(baseDn, null);
if (sdomain == null)
{
@@ -3714,15 +2508,6 @@
return retrievesBackend(baseDn);
}
- /**
- * Returns a boolean indicating if an import or export is currently
- * processed.
- * @return The status
- */
- public boolean ieRunning()
- {
- return (ieContext != null);
- }
/*
* <<Total Update
*/
@@ -3769,7 +2554,7 @@
{
// Check that there is not already a domain with the same DN
DN dn = configuration.getBaseDN();
- ReplicationDomain domain = MultimasterReplication.findDomain(dn, null);
+ LDAPReplicationDomain domain = MultimasterReplication.findDomain(dn, null);
if ((domain != null) && (domain.baseDn.equals(dn)))
{
Message message = ERR_SYNC_INVALID_DN.get();
@@ -3793,37 +2578,12 @@
public ConfigChangeResult applyConfigurationChange(
ReplicationDomainCfg configuration)
{
- // server id and base dn are readonly.
- // isolationPolicy can be set immediately and will apply
- // to the next updates.
- // The other parameters needs to be renegociated with the ReplicationServer
- // so that requires restarting the session with the ReplicationServer.
- Boolean needToRestartSession = false;
- Collection<String> newReplServers = configuration.getReplicationServer();
-
- // A new session is necessary only when information regarding
- // the connection is modified
- if ((!(replicationServers.size() == newReplServers.size()
- && replicationServers.containsAll(newReplServers))) ||
- window != configuration.getWindowSize() ||
- heartbeatInterval != configuration.getHeartbeatInterval())
- needToRestartSession = true;
-
- replicationServers = newReplServers;
- window = configuration.getWindowSize();
- heartbeatInterval = configuration.getHeartbeatInterval();
- broker.changeConfig(replicationServers, maxReceiveQueue, maxReceiveDelay,
- maxSendQueue, maxSendDelay, window, heartbeatInterval);
isolationpolicy = configuration.getIsolationPolicy();
- // To be able to stop and restart the broker properly just
- // disable and enable the domain. That way a new session
- // with the new configuration is available.
- if (needToRestartSession)
- {
- this.disable();
- this.enable();
- }
+ changeConfig(
+ configuration.getReplicationServer(),
+ configuration.getWindowSize(),
+ configuration.getHeartbeatInterval());
return new ConfigChangeResult(ResultCode.SUCCESS, false);
}
@@ -3867,101 +2627,265 @@
}
/**
- * Check if the domain is connected to a ReplicationServer.
+ * Starts the Replication Domain.
+ */
+ public void start()
+ {
+ // Create the ServerStateFlush thread
+ flushThread = new ServerStateFlush();
+ flushThread.start();
+
+ startListenService();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void sessionInitiated(
+ ServerStatus initStatus,
+ ServerState replicationServerState,
+ ProtocolSession session)
+ {
+ super.sessionInitiated(initStatus, replicationServerState, session);
+ try
+ {
+ /*
+ * We must not publish changes to a replicationServer that has
+ * not seen all our previous changes because this could cause
+ * some other ldap servers to miss those changes.
+ * Check that the ReplicationServer has seen all our previous
+ * changes.
+ */
+ ChangeNumber replServerMaxChangeNumber =
+ replicationServerState.getMaxChangeNumber(serverId);
+
+ if (replServerMaxChangeNumber == null)
+ {
+ replServerMaxChangeNumber = new ChangeNumber(0, 0, serverId);
+ }
+ ChangeNumber ourMaxChangeNumber =
+ state.getMaxChangeNumber(serverId);
+
+ if ((ourMaxChangeNumber != null) &&
+ (!ourMaxChangeNumber.olderOrEqual(replServerMaxChangeNumber)))
+ {
+
+ // Replication server is missing some of our changes: let's
+ // send them to him.
+
+ Message message = DEBUG_GOING_TO_SEARCH_FOR_CHANGES.get();
+ logError(message);
+
+ /*
+ * Get all the changes that have not been seen by this
+ * replication server and populate the replayOperations
+ * list.
+ */
+ InternalSearchOperation op = searchForChangedEntries(
+ baseDn, replServerMaxChangeNumber, this);
+ if (op.getResultCode() != ResultCode.SUCCESS)
+ {
+ /*
+ * An error happened trying to search for the updates
+ * This server will start accepting again new updates but
+ * some inconsistencies will stay between servers.
+ * Log an error for the repair tool
+ * that will need to re-synchronize the servers.
+ */
+ message = ERR_CANNOT_RECOVER_CHANGES.get(
+ baseDn.toNormalizedString());
+ logError(message);
+ } else
+ {
+ for (FakeOperation replayOp : replayOperations)
+ {
+ ChangeNumber cn = replayOp.getChangeNumber();
+ /*
+ * Because the entry returned by the search operation
+ * can contain old historical information, it is
+ * possible that some of the FakeOperation are
+ * actually older than the
+ * Only send the Operation if it was newer than
+ * the last ChangeNumber known by the Replication Server.
+ */
+ if (cn.newer(replServerMaxChangeNumber))
+ {
+ message =
+ DEBUG_SENDING_CHANGE.get(
+ replayOp.getChangeNumber().toString());
+ logError(message);
+ session.publish(replayOp.generateMessage());
+ }
+ }
+ message = DEBUG_CHANGES_SENT.get();
+ logError(message);
+ }
+ replayOperations.clear();
+ }
+ } catch (Exception e)
+ {
+ Message message = ERR_PUBLISHING_FAKE_OPS.get(
+ baseDn.toNormalizedString(),
+ e.getLocalizedMessage() + stackTraceToSingleLineString(e));
+ logError(message);
+ }
+ }
+
+ /**
+ * Search for the changes that happened since fromChangeNumber
+ * based on the historical attribute. The only changes that will
+ * be send will be the one generated on the serverId provided in
+ * fromChangeNumber.
+ * @param baseDn the base DN
+ * @param fromChangeNumber The change number from which we want the changes
+ * @param resultListener that will process the entries returned.
+ * @return the internal search operation
+ * @throws Exception when raised.
+ */
+ public static InternalSearchOperation searchForChangedEntries(
+ DN baseDn,
+ ChangeNumber fromChangeNumber,
+ InternalSearchListener resultListener)
+ throws Exception
+ {
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ Short serverId = fromChangeNumber.getServerId();
+
+ String maxValueForId = "ffffffffffffffff" +
+ String.format("%04x", serverId) + "ffffffff";
+
+ LDAPFilter filter = LDAPFilter.decode(
+ "(&(" + Historical.HISTORICALATTRIBUTENAME + ">=dummy:"
+ + fromChangeNumber + ")(" + Historical.HISTORICALATTRIBUTENAME +
+ "<=dummy:" + maxValueForId + "))");
+
+ LinkedHashSet<String> attrs = new LinkedHashSet<String>(1);
+ attrs.add(Historical.HISTORICALATTRIBUTENAME);
+ attrs.add(Historical.ENTRYUIDNAME);
+ attrs.add("*");
+ return conn.processSearch(
+ new ASN1OctetString(baseDn.toString()),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES,
+ 0, 0, false, filter,
+ attrs,
+ resultListener);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleInternalSearchEntry(
+ InternalSearchOperation searchOperation,
+ SearchResultEntry searchEntry)
+ {
+ /*
+ * This call back is called at session establishment phase
+ * for each entry that has been changed by this server and the changes
+ * have not been sent to any Replication Server.
+ * The role of this method is to build equivalent operation from
+ * the historical information and add them in the replayOperations
+ * table.
+ */
+ Iterable<FakeOperation> updates =
+ Historical.generateFakeOperations(searchEntry);
+ for (FakeOperation op : updates)
+ {
+ replayOperations.add(op);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleInternalSearchReference(
+ InternalSearchOperation searchOperation,
+ SearchResultReference searchReference)
+ {
+ // TODO to be implemented
+ }
+
+
+ /**
+ * This method should return the total number of objects in the
+ * replicated domain.
+ * This count will be used for reporting.
*
- * @return true if the server is connected, false if not.
+ * @throws DirectoryException when needed.
+ *
+ * @return The number of objects in the replication domain.
*/
- public boolean isConnected()
+ public long countEntries() throws DirectoryException
{
- if (broker != null)
- return broker.isConnected();
- else
+ Backend backend = retrievesBackend(baseDn);
+
+ if (!backend.supportsLDIFExport())
+ {
+ Message message = ERR_INIT_EXPORT_NOT_SUPPORTED.get(
+ backend.getBackendID().toString());
+ logError(message);
+ throw new DirectoryException(ResultCode.OTHER, message);
+ }
+
+ return backend.numSubordinates(baseDn, true) + 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean processUpdate(UpdateMsg updateMsg)
+ {
+ if (updateMsg instanceof LDAPUpdateMsg)
+ {
+ LDAPUpdateMsg msg = (LDAPUpdateMsg) updateMsg;
+
+ // put the UpdateMsg in the RemotePendingChanges list.
+ remotePendingChanges.putRemoteUpdate(msg);
+
+ // Put update message into the replay queue
+ // (block until some place in the queue is available)
+ UpdateToReplay updateToReplay = new UpdateToReplay(msg, this);
+ updateToReplayQueue.offer(updateToReplay);
+
return false;
+ }
+
+ // unknown message type, this should not happen, just ignore it.
+ return true;
}
/**
- * Determine whether the connection to the replication server is encrypted.
- * @return true if the connection is encrypted, false otherwise.
+ * Monitoring information for the LDAPReplicationDomain.
+ *
+ * @return Monitoring attributes specific to the LDAPReplicationDomain.
*/
- public boolean isSessionEncrypted()
+ public Collection<Attribute> getAdditionalMonitoring()
{
- if (broker != null)
- return broker.isSessionEncrypted();
- else
- return false;
- }
+ ArrayList<Attribute> attributes = new ArrayList<Attribute>();
- /**
- * Gets the info for DSs in the topology (except us).
- * @return The info for DSs in the topology (except us)
- */
- public List<DSInfo> getDsList()
- {
- return dsList;
- }
+ /* get number of changes in the pending list */
+ ReplicationMonitor.addMonitorData(
+ attributes, "pending-updates", getPendingUpdatesCount());
- /**
- * Gets the info for RSs in the topology (except the one we are connected
- * to).
- * @return The info for RSs in the topology (except the one we are connected
- * to)
- */
- public List<RSInfo> getRsList()
- {
- return rsList;
- }
+ /* get number of changes successfully */
+ ReplicationMonitor.addMonitorData(attributes, "replayed-updates-ok",
+ getNumReplayedPostOpCalled());
- /**
- * Tells if assured replication is enabled for this domain.
- * @return True if assured replication is enabled for this domain.
- */
- public boolean isAssured()
- {
- return assured;
- }
+ /* get number of modify conflicts */
+ ReplicationMonitor.addMonitorData(attributes, "resolved-modify-conflicts",
+ getNumResolvedModifyConflicts());
- /**
- * Gives the mode for the assured replication of the domain.
- * @return The mode for the assured replication of the domain.
- */
- public AssuredMode getAssuredMode()
- {
- return assuredMode;
- }
+ /* get number of naming conflicts */
+ ReplicationMonitor.addMonitorData(attributes, "resolved-naming-conflicts",
+ getNumResolvedNamingConflicts());
- /**
- * Gives the assured level of the replication of the domain.
- * @return The assured level of the replication of the domain.
- */
- public byte getAssuredSdLevel()
- {
- return assuredSdLevel;
- }
+ /* get number of unresolved naming conflicts */
+ ReplicationMonitor.addMonitorData(attributes, "unresolved-naming-conflicts",
+ getNumUnresolvedNamingConflicts());
- /**
- * Gets the group id for this domain.
- * @return The group id for this domain.
- */
- public byte getGroupId()
- {
- return groupId;
- }
-
- /**
- * Gets the referrals URLs this domain publishes.
- * @return The referrals URLs this domain publishes.
- */
- public List<String> getRefUrls()
- {
- return refUrls;
- }
-
- /**
- * Gets the status for this domain.
- * @return The status for this domain.
- */
- public ServerStatus getStatus()
- {
- return status;
+ return attributes;
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java
index 0af98a0..bbc0a82 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java
@@ -91,8 +91,8 @@
ExportTaskListener
{
private ReplicationServerListener replicationServerListener = null;
- private static Map<DN, ReplicationDomain> domains =
- new HashMap<DN, ReplicationDomain>() ;
+ private static Map<DN, LDAPReplicationDomain> domains =
+ new HashMap<DN, LDAPReplicationDomain>() ;
/**
* The queue of received update messages, to be treated by the ReplayThread
@@ -122,7 +122,8 @@
* Can be null is the request has no associated operation.
* @return The domain for this DN.
*/
- public static ReplicationDomain findDomain(DN dn, PluginOperation pluginOp)
+ public static LDAPReplicationDomain findDomain(
+ DN dn, PluginOperation pluginOp)
{
/*
* Don't run the special replication code on Operation that are
@@ -163,7 +164,7 @@
}
- ReplicationDomain domain = null;
+ LDAPReplicationDomain domain = null;
DN temp = dn;
do
{
@@ -186,12 +187,12 @@
* @return The domain created.
* @throws ConfigException When the configuration is not valid.
*/
- public static ReplicationDomain createNewDomain(
+ public static LDAPReplicationDomain createNewDomain(
ReplicationDomainCfg configuration)
throws ConfigException
{
- ReplicationDomain domain;
- domain = new ReplicationDomain(configuration, updateToReplayQueue);
+ LDAPReplicationDomain domain;
+ domain = new LDAPReplicationDomain(configuration, updateToReplayQueue);
if (domains.size() == 0)
{
@@ -211,7 +212,7 @@
*/
public static void deleteDomain(DN dn)
{
- ReplicationDomain domain = domains.remove(dn);
+ LDAPReplicationDomain domain = domains.remove(dn);
if (domain != null)
domain.shutdown();
@@ -310,7 +311,7 @@
public boolean isConfigurationAddAcceptable(
ReplicationDomainCfg configuration, List<Message> unacceptableReasons)
{
- return ReplicationDomain.isConfigurationAcceptable(
+ return LDAPReplicationDomain.isConfigurationAcceptable(
configuration, unacceptableReasons);
}
@@ -322,7 +323,7 @@
{
try
{
- ReplicationDomain rd = createNewDomain(configuration);
+ LDAPReplicationDomain rd = createNewDomain(configuration);
if (isRegistered)
{
rd.start();
@@ -384,7 +385,7 @@
public SynchronizationProviderResult handleConflictResolution(
PreOperationModifyOperation modifyOperation)
{
- ReplicationDomain domain =
+ LDAPReplicationDomain domain =
findDomain(modifyOperation.getEntryDN(), modifyOperation);
if (domain == null)
return new SynchronizationProviderResult.ContinueProcessing();
@@ -399,7 +400,7 @@
public SynchronizationProviderResult handleConflictResolution(
PreOperationAddOperation addOperation) throws DirectoryException
{
- ReplicationDomain domain =
+ LDAPReplicationDomain domain =
findDomain(addOperation.getEntryDN(), addOperation);
if (domain == null)
return new SynchronizationProviderResult.ContinueProcessing();
@@ -414,7 +415,7 @@
public SynchronizationProviderResult handleConflictResolution(
PreOperationDeleteOperation deleteOperation) throws DirectoryException
{
- ReplicationDomain domain =
+ LDAPReplicationDomain domain =
findDomain(deleteOperation.getEntryDN(), deleteOperation);
if (domain == null)
return new SynchronizationProviderResult.ContinueProcessing();
@@ -429,7 +430,7 @@
public SynchronizationProviderResult handleConflictResolution(
PreOperationModifyDNOperation modifyDNOperation) throws DirectoryException
{
- ReplicationDomain domain =
+ LDAPReplicationDomain domain =
findDomain(modifyDNOperation.getEntryDN(), modifyDNOperation);
if (domain == null)
return new SynchronizationProviderResult.ContinueProcessing();
@@ -445,7 +446,7 @@
doPreOperation(PreOperationModifyOperation modifyOperation)
{
DN operationDN = modifyOperation.getEntryDN();
- ReplicationDomain domain = findDomain(operationDN, modifyOperation);
+ LDAPReplicationDomain domain = findDomain(operationDN, modifyOperation);
if ((domain == null) || (!domain.solveConflict()))
return new SynchronizationProviderResult.ContinueProcessing();
@@ -485,7 +486,7 @@
throws DirectoryException
{
DN operationDN = modifyDNOperation.getEntryDN();
- ReplicationDomain domain = findDomain(operationDN, modifyDNOperation);
+ LDAPReplicationDomain domain = findDomain(operationDN, modifyDNOperation);
if ((domain == null) || (!domain.solveConflict()))
return new SynchronizationProviderResult.ContinueProcessing();
@@ -513,7 +514,7 @@
public SynchronizationProviderResult doPreOperation(
PreOperationAddOperation addOperation)
{
- ReplicationDomain domain =
+ LDAPReplicationDomain domain =
findDomain(addOperation.getEntryDN(), addOperation);
if (domain == null)
return new SynchronizationProviderResult.ContinueProcessing();
@@ -534,7 +535,7 @@
isRegistered = false;
// shutdown all the domains
- for (ReplicationDomain domain : domains.values())
+ for (LDAPReplicationDomain domain : domains.values())
{
domain.shutdown();
}
@@ -566,7 +567,7 @@
@Override
public void processSchemaChange(List<Modification> modifications)
{
- ReplicationDomain domain =
+ LDAPReplicationDomain domain =
findDomain(DirectoryServer.getSchemaDN(), null);
if (domain != null)
domain.synchronizeModifications(modifications);
@@ -579,7 +580,7 @@
{
for (DN dn : backend.getBaseDNs())
{
- ReplicationDomain domain = findDomain(dn, null);
+ LDAPReplicationDomain domain = findDomain(dn, null);
if (domain != null)
domain.backupStart();
}
@@ -593,7 +594,7 @@
{
for (DN dn : backend.getBaseDNs())
{
- ReplicationDomain domain = findDomain(dn, null);
+ LDAPReplicationDomain domain = findDomain(dn, null);
if (domain != null)
domain.backupEnd();
}
@@ -606,7 +607,7 @@
{
for (DN dn : backend.getBaseDNs())
{
- ReplicationDomain domain = findDomain(dn, null);
+ LDAPReplicationDomain domain = findDomain(dn, null);
if (domain != null)
domain.disable();
}
@@ -620,7 +621,7 @@
{
for (DN dn : backend.getBaseDNs())
{
- ReplicationDomain domain = findDomain(dn, null);
+ LDAPReplicationDomain domain = findDomain(dn, null);
if (domain != null)
domain.enable();
}
@@ -633,7 +634,7 @@
{
for (DN dn : backend.getBaseDNs())
{
- ReplicationDomain domain = findDomain(dn, null);
+ LDAPReplicationDomain domain = findDomain(dn, null);
if (domain != null)
domain.disable();
}
@@ -647,7 +648,7 @@
{
for (DN dn : backend.getBaseDNs())
{
- ReplicationDomain domain = findDomain(dn, null);
+ LDAPReplicationDomain domain = findDomain(dn, null);
if (domain != null)
domain.enable();
}
@@ -660,7 +661,7 @@
{
for (DN dn : backend.getBaseDNs())
{
- ReplicationDomain domain = findDomain(dn, null);
+ LDAPReplicationDomain domain = findDomain(dn, null);
if (domain != null)
domain.backupStart();
}
@@ -674,7 +675,7 @@
{
for (DN dn : backend.getBaseDNs())
{
- ReplicationDomain domain = findDomain(dn, null);
+ LDAPReplicationDomain domain = findDomain(dn, null);
if (domain != null)
domain.backupEnd();
}
@@ -708,7 +709,7 @@
*/
private void genericPostOperation(PostOperationOperation operation, DN dn)
{
- ReplicationDomain domain = findDomain(dn, operation);
+ LDAPReplicationDomain domain = findDomain(dn, operation);
if (domain == null)
return;
@@ -766,7 +767,7 @@
isRegistered = true;
// start all the domains
- for (ReplicationDomain domain : domains.values())
+ for (LDAPReplicationDomain domain : domains.values())
{
domain.start();
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PendingChange.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PendingChange.java
index 2ff4904..0bc994e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PendingChange.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PendingChange.java
@@ -28,7 +28,7 @@
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.common.ServerState;
-import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.operation.PluginOperation;
@@ -41,7 +41,7 @@
{
private ChangeNumber changeNumber;
private boolean committed;
- private UpdateMsg msg;
+ private LDAPUpdateMsg msg;
private PluginOperation op;
private ServerState dependencyState = null;
private DN targetDN = null;
@@ -54,7 +54,7 @@
*/
public PendingChange(ChangeNumber changeNumber,
PluginOperation op,
- UpdateMsg msg)
+ LDAPUpdateMsg msg)
{
this.changeNumber = changeNumber;
this.committed = false;
@@ -94,7 +94,7 @@
* @return the message if operation was a replication operation
* null if the operation was a local operation
*/
- public UpdateMsg getMsg()
+ public LDAPUpdateMsg getMsg()
{
return msg;
}
@@ -103,7 +103,7 @@
* Set the message associated to the PendingChange.
* @param msg the message
*/
- public void setMsg(UpdateMsg msg)
+ public void setMsg(LDAPUpdateMsg msg)
{
this.msg = msg;
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PendingChanges.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PendingChanges.java
index 513e41c..a284aae 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PendingChanges.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PendingChanges.java
@@ -26,14 +26,19 @@
*/
package org.opends.server.replication.plugin;
+import static org.opends.messages.ReplicationMessages.*;
+import static org.opends.server.loggers.ErrorLogger.logError;
+
import java.util.NoSuchElementException;
import java.util.SortedMap;
import java.util.TreeMap;
+import java.util.concurrent.TimeoutException;
+import org.opends.messages.Message;
+import org.opends.server.replication.service.ReplicationDomain;
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.common.ChangeNumberGenerator;
-import org.opends.server.replication.common.ServerState;
-import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
import org.opends.server.types.operation.PluginOperation;
/**
@@ -61,32 +66,23 @@
private ChangeNumberGenerator changeNumberGenerator;
/**
- * The Replicationbroker that will be used to send UpdateMsg.
+ * The ReplicationDomain that will be used to send UpdateMsg.
*/
- private ReplicationBroker broker;
-
- /**
- * The ServerState that will be updated when UpdateMsg are committed.
- */
- private ServerState state;
+ private ReplicationDomain domain;
/**
* Creates a new PendingChanges using the provided ChangeNumberGenerator.
*
* @param changeNumberGenerator The ChangeNumberGenerator to use to create
* new unique ChangeNumbers.
- * @param broker The Replicationbroker that will be used to send
+ * @param domain The ReplicationDomain that will be used to send
* UpdateMsg.
- * @param state The ServerState that will be updated when UpdateMsg
- * are committed.
*/
public PendingChanges(
- ChangeNumberGenerator changeNumberGenerator, ReplicationBroker broker,
- ServerState state)
+ ChangeNumberGenerator changeNumberGenerator, ReplicationDomain domain)
{
this.changeNumberGenerator = changeNumberGenerator;
- this.broker = broker;
- this.state = state;
+ this.domain = domain;
}
/**
@@ -96,7 +92,7 @@
*
* @return The UpdateMsg that was just removed.
*/
- public synchronized UpdateMsg remove(ChangeNumber changeNumber)
+ public synchronized LDAPUpdateMsg remove(ChangeNumber changeNumber)
{
return pendingChanges.remove(changeNumber).getMsg();
}
@@ -119,7 +115,7 @@
* @param msg The message associated to the update.
*/
public synchronized void commit(ChangeNumber changeNumber,
- UpdateMsg msg)
+ LDAPUpdateMsg msg)
{
PendingChange curChange = pendingChanges.get(changeNumber);
if (curChange == null)
@@ -186,9 +182,19 @@
(firstChange.getOp().isSynchronizationOperation() == false))
{
numSentUpdates++;
- broker.publish(firstChange.getMsg());
- }
- state.update(firstChangeNumber);
+ LDAPUpdateMsg updateMsg = firstChange.getMsg();
+ try
+ {
+ domain.publish(updateMsg);
+ } catch (TimeoutException ex) {
+ // This exception may only be raised if assured replication is
+ // enabled
+ Message errorMsg = ERR_DS_ACK_TIMEOUT.get(
+ domain.getServiceID(), Long.toString(domain.getAssuredTimeout()),
+ updateMsg.toString());
+ logError(errorMsg);
+ }
+ }
pendingChanges.remove(firstChangeNumber);
if (pendingChanges.isEmpty())
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
index 8d74618..22f526c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
@@ -66,14 +66,15 @@
* used to store the synchronized data and that is therefore persistent
* across server reboot.
*/
-public class PersistentServerState extends ServerState
+public class PersistentServerState
{
- private DN baseDn;
- private boolean savedStatus = true;
- private InternalClientConnection conn =
+ private final DN baseDn;
+ private final InternalClientConnection conn =
InternalClientConnection.getRootConnection();
- private ASN1OctetString asn1BaseDn;
- private short serverId;
+ private final ASN1OctetString asn1BaseDn;
+ private final short serverId;
+
+ private final ServerState state;
/**
* The attribute name used to store the state in the backend.
@@ -83,24 +84,45 @@
/**
* create a new ServerState.
* @param baseDn The baseDN for which the ServerState is created
- * @param serverId The serverId
+ * @param serverId The serverId
*/
public PersistentServerState(DN baseDn, short serverId)
{
this.baseDn = baseDn;
this.serverId = serverId;
+ this.state = new ServerState();
asn1BaseDn = new ASN1OctetString(baseDn.toString());
loadState();
}
/**
- * {@inheritDoc}
+ * Create a new PersistenServerState based on an already existing ServerState.
+ *
+ * @param baseDn The baseDN for which the ServerState is created.
+ * @param serverId The serverId.
+ * @param state The serverState.
*/
- @Override
+ public PersistentServerState(DN baseDn, short serverId, ServerState state)
+ {
+ this.baseDn = baseDn;
+ this.serverId = serverId;
+ this.state = state;
+ asn1BaseDn = new ASN1OctetString(baseDn.toString());
+ loadState();
+ }
+
+ /**
+ * Update the Server State with a ChangeNumber.
+ * All operations with smaller CSN and the same serverID must be committed
+ * before calling this method.
+ *
+ * @param changeNumber The committed ChangeNumber.
+ *
+ * @return a boolean indicating if the update was meaningful.
+ */
public boolean update(ChangeNumber changeNumber)
{
- savedStatus = false;
- return super.update(changeNumber);
+ return state.update(changeNumber);
}
/**
@@ -108,14 +130,14 @@
*/
public void save()
{
- if (savedStatus)
+ if (state.isSaved())
return;
- savedStatus = true;
+ state.setSaved(true);
ResultCode resultCode = updateStateEntry();
if (resultCode != ResultCode.SUCCESS)
{
- savedStatus = false;
+ state.setSaved(false);
}
}
@@ -310,7 +332,7 @@
*/
private ResultCode runUpdateStateEntry(DN serverStateEntryDN)
{
- ArrayList<ASN1OctetString> values = this.toASN1ArrayList();
+ ArrayList<ASN1OctetString> values = state.toASN1ArrayList();
LDAPAttribute attr =
new LDAPAttribute(REPLICATION_STATE, values);
@@ -347,8 +369,8 @@
*/
public void clearInMemory()
{
- super.clear();
- this.savedStatus = false;
+ state.clear();
+ state.setSaved(false);
}
/**
@@ -384,13 +406,13 @@
// maxCn stored in the serverState
synchronized (this)
{
- serverStateMaxCn = this.getMaxChangeNumber(serverId);
+ serverStateMaxCn = state.getMaxChangeNumber(serverId);
if (serverStateMaxCn == null)
return;
try {
- op = ReplicationBroker.searchForChangedEntries(baseDn,
+ op = LDAPReplicationDomain.searchForChangedEntries(baseDn,
serverStateMaxCn, null);
}
catch (Exception e)
@@ -448,4 +470,16 @@
}
}
}
+
+ /**
+ * Get the largest ChangeNumber seen for a given LDAP server ID.
+ *
+ * @param serverID The server ID.
+ *
+ * @return The largest ChangeNumber seen.
+ */
+ public ChangeNumber getMaxChangeNumber(short serverID)
+ {
+ return state.getMaxChangeNumber(serverID);
+ }
}
\ No newline at end of file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java
index 3552aa6..9c7f7ae 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java
@@ -37,13 +37,12 @@
import org.opends.server.core.ModifyDNOperationBasis;
import org.opends.server.core.ModifyOperation;
import org.opends.server.replication.common.ChangeNumber;
-import org.opends.server.replication.common.ChangeNumberGenerator;
import org.opends.server.replication.common.ServerState;
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.OperationContext;
-import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
import org.opends.server.types.DN;
import org.opends.server.types.Operation;
@@ -76,42 +75,31 @@
new TreeSet<PendingChange>();
/**
- * The ServerState that will be updated when UpdateMsg are fully replayed.
+ * The ServerState that will be updated when LDAPUpdateMsg are fully replayed.
*/
private ServerState state;
/**
- * The ChangeNumberGenerator to must be adjusted when new changes
- * are received from a remote server.
- */
- private ChangeNumberGenerator changeNumberGenerator;
-
- /**
* Creates a new RemotePendingChanges using the provided ServerState.
*
- * @param changeNumberGenerator The ChangeNumberGenerator that should
- * be adjusted when changes are received.
- * @param state The ServerState that will be updated when UpdateMsg
+ * @param state The ServerState that will be updated when LDAPUpdateMsg
* have been fully replayed.
*/
- public RemotePendingChanges(ChangeNumberGenerator changeNumberGenerator,
- ServerState state)
+ public RemotePendingChanges(ServerState state)
{
- this.changeNumberGenerator = changeNumberGenerator;
this.state = state;
}
/**
- * Add a new UpdateMsg that was received from the replication server
+ * Add a new LDAPUpdateMsg that was received from the replication server
* to the pendingList.
*
- * @param update The UpdateMsg that was received from the replication
+ * @param update The LDAPUpdateMsg that was received from the replication
* server and that will be added to the pending list.
*/
- public synchronized void putRemoteUpdate(UpdateMsg update)
+ public synchronized void putRemoteUpdate(LDAPUpdateMsg update)
{
ChangeNumber changeNumber = update.getChangeNumber();
- changeNumberGenerator.adjust(changeNumber);
pendingChanges.put(changeNumber, new PendingChange(changeNumber, null,
update));
}
@@ -154,9 +142,9 @@
/**
* Get the first update in the list that have some dependencies cleared.
*
- * @return The UpdateMsg to be handled.
+ * @return The LDAPUpdateMsg to be handled.
*/
- public synchronized UpdateMsg getNextUpdate()
+ public synchronized LDAPUpdateMsg getNextUpdate()
{
/*
* Parse the list of Update with dependencies and check if the dependencies
@@ -216,7 +204,7 @@
{
if (pendingChange.getChangeNumber().older(changeNumber))
{
- UpdateMsg pendingMsg = pendingChange.getMsg();
+ LDAPUpdateMsg pendingMsg = pendingChange.getMsg();
if (pendingMsg != null)
{
if (pendingMsg instanceof DeleteMsg)
@@ -297,7 +285,7 @@
{
if (pendingChange.getChangeNumber().older(changeNumber))
{
- UpdateMsg pendingMsg = pendingChange.getMsg();
+ LDAPUpdateMsg pendingMsg = pendingChange.getMsg();
if (pendingMsg != null)
{
if (pendingMsg instanceof AddMsg)
@@ -349,7 +337,7 @@
{
if (pendingChange.getChangeNumber().older(changeNumber))
{
- UpdateMsg pendingMsg = pendingChange.getMsg();
+ LDAPUpdateMsg pendingMsg = pendingChange.getMsg();
if (pendingMsg != null)
{
if (pendingMsg instanceof DeleteMsg)
@@ -424,7 +412,7 @@
{
if (pendingChange.getChangeNumber().older(changeNumber))
{
- UpdateMsg pendingMsg = pendingChange.getMsg();
+ LDAPUpdateMsg pendingMsg = pendingChange.getMsg();
if (pendingMsg != null)
{
if (pendingMsg instanceof DeleteMsg)
@@ -479,7 +467,7 @@
* @return A boolean indicating if an operation cannot be replayed
* because of dependencies.
*/
- public boolean checkDependencies(Operation op, UpdateMsg msg)
+ public boolean checkDependencies(Operation op, LDAPUpdateMsg msg)
{
if (op instanceof ModifyOperation)
{
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplLDIFOutputStream.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplLDIFOutputStream.java
index a5f6700..402e014 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplLDIFOutputStream.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplLDIFOutputStream.java
@@ -30,6 +30,7 @@
import java.io.IOException;
import java.io.OutputStream;
+import org.opends.server.replication.service.ReplicationDomain;
import org.opends.server.util.ServerConstants;
/**
@@ -103,8 +104,7 @@
// of entries to export.
throw(new IOException());
}
- if (numEntries<0)
- domain.exportLDIFEntry(entryBuffer);
+
numExportedEntries++;
entryBuffer = "";
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplayThread.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplayThread.java
index 8993a13..1899935 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplayThread.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplayThread.java
@@ -25,6 +25,8 @@
* Copyright 2006-2008 Sun Microsystems, Inc.
*/
package org.opends.server.replication.plugin;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
+
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.opends.messages.Message;
@@ -37,7 +39,6 @@
import org.opends.server.api.DirectoryThread;
import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.replication.protocol.UpdateMsg;
/**
* Thread that is used to get message from the replication servers (stored
@@ -102,8 +103,8 @@
TimeUnit.SECONDS)) != null))
{
// Find replication domain for that update message
- UpdateMsg updateMsg = updateToreplay.getUpdateMessage();
- ReplicationDomain domain = updateToreplay.getReplicationDomain();
+ LDAPUpdateMsg updateMsg = updateToreplay.getUpdateMessage();
+ LDAPReplicationDomain domain = updateToreplay.getReplicationDomain();
domain.replay(updateMsg);
}
} catch (Exception e)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplicationMonitor.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplicationMonitor.java
deleted file mode 100644
index 005747e..0000000
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplicationMonitor.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- * Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- * Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package org.opends.server.replication.plugin;
-
-import java.util.ArrayList;
-
-import org.opends.server.admin.std.server.MonitorProviderCfg;
-import org.opends.server.api.MonitorProvider;
-import org.opends.server.core.DirectoryServer;
-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;
-
-/**
- * Class used to generate monitoring information for the replication.
- */
-public class ReplicationMonitor extends MonitorProvider<MonitorProviderCfg>
-{
- private ReplicationDomain domain; // the replication plugin
-
- /**
- * Create a new replication monitor.
- * @param domain the plugin which created the monitor
- */
- public ReplicationMonitor(ReplicationDomain domain)
- {
- super("Replication monitor " + domain.getBaseDN().toString());
- this.domain = domain;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override()
- public void initializeMonitorProvider(MonitorProviderCfg configuration)
- {
- // no implementation needed.
- }
-
- /**
- * Retrieves the name of this monitor provider. It should be unique among all
- * monitor providers, including all instances of the same monitor provider.
- *
- * @return The name of this monitor provider.
- */
- @Override
- public String getMonitorInstanceName()
- {
- return "Replication plugin " + domain.getBaseDN().toString();
- }
-
- /**
- * Retrieves a set of attributes containing monitor data that should be
- * returned to the client if the corresponding monitor entry is requested.
- *
- * @return A set of attributes containing monitor data that should be
- * returned to the client if the corresponding monitor entry is
- * requested.
- */
- @Override
- public ArrayList<Attribute> getMonitorData()
- {
- ArrayList<Attribute> attributes = new ArrayList<Attribute>();
-
- /* get the base dn */
- Attribute attr = Attributes.create("base-dn", domain.getBaseDN()
- .toString());
- attributes.add(attr);
-
- /* get the base dn */
- attr = Attributes.create("connected-to", domain
- .getReplicationServer());
- attributes.add(attr);
-
- /* get number of lost connections */
- addMonitorData(attributes, "lost-connections",
- domain.getNumLostConnections());
-
- /* get number of received updates */
- addMonitorData(attributes, "received-updates", domain.getNumRcvdUpdates());
-
- /* get number of updates sent */
- addMonitorData(attributes, "sent-updates", domain.getNumSentUpdates());
-
- /* get number of changes in the pending list */
- addMonitorData(attributes, "pending-updates",
- domain.getPendingUpdatesCount());
-
- /* get number of changes replayed */
- addMonitorData(attributes, "replayed-updates",
- domain.getNumProcessedUpdates());
-
- /* get number of changes successfully */
- addMonitorData(attributes, "replayed-updates-ok",
- domain.getNumReplayedPostOpCalled());
-
- /* get number of modify conflicts */
- addMonitorData(attributes, "resolved-modify-conflicts",
- domain.getNumResolvedModifyConflicts());
-
- /* get number of naming conflicts */
- addMonitorData(attributes, "resolved-naming-conflicts",
- domain.getNumResolvedNamingConflicts());
-
- /* get number of unresolved naming conflicts */
- addMonitorData(attributes, "unresolved-naming-conflicts",
- domain.getNumUnresolvedNamingConflicts());
-
- /* get server-id */
- addMonitorData(attributes, "server-id",
- domain.getServerId());
-
- /* get window information */
- addMonitorData(attributes, "max-rcv-window", domain.getMaxRcvWindow());
- addMonitorData(attributes, "current-rcv-window",
- domain.getCurrentRcvWindow());
- addMonitorData(attributes, "max-send-window",
- domain.getMaxSendWindow());
- addMonitorData(attributes, "current-send-window",
- domain.getCurrentSendWindow());
-
- /* get the Server State */
- final String ATTR_SERVER_STATE = "server-state";
- AttributeType type =
- DirectoryServer.getDefaultAttributeType(ATTR_SERVER_STATE);
- AttributeBuilder builder = new AttributeBuilder(type, ATTR_SERVER_STATE);
- for (String str : domain.getServerState().toStringSet())
- {
- builder.add(new AttributeValue(type,str));
- }
- attributes.add(builder.toAttribute());
-
- attributes.add(Attributes.create("ssl-encryption",
- String.valueOf(domain.isSessionEncrypted())));
-
- attributes.add(Attributes.create("generation-id",
- String.valueOf(domain.getGenerationId())));
-
- return attributes;
-
- }
-
- /**
- * Add an attribute with an integer value to the list of monitoring
- * attributes.
- *
- * @param attributes the list of monitoring attributes
- * @param name the name of the attribute to add.
- * @param value The integer value of he attribute to add.
- */
- private void addMonitorData(ArrayList<Attribute> attributes, String name,
- int value)
- {
- AttributeType type = DirectoryServer.getDefaultAttributeType(name);
- attributes.add(Attributes.create(type, new AttributeValue(type,
- String.valueOf(value))));
- }
-
- /**
- * Retrieves the length of time in milliseconds that should elapse between
- * calls to the <CODE>updateMonitorData()</CODE> method. A negative or zero
- * return value indicates that the <CODE>updateMonitorData()</CODE> method
- * should not be periodically invoked.
- *
- * @return The length of time in milliseconds that should elapse between
- * calls to the <CODE>updateMonitorData()</CODE> method.
- */
- @Override
- public long getUpdateInterval()
- {
- /* we don't wont to do polling on this monitor */
- return 0;
- }
-
- /**
- * Performs any processing periodic processing that may be desired to update
- * the information associated with this monitor. Note that best-effort
- * attempts will be made to ensure that calls to this method come
- * <CODE>getUpdateInterval()</CODE> milliseconds apart, but no guarantees will
- * be made.
- */
- @Override
- public void updateMonitorData()
- {
- // As long as getUpdateInterval() returns 0, this will never get called
- }
-}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/UpdateToReplay.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/UpdateToReplay.java
index 55fcce6..beb9690 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/UpdateToReplay.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/UpdateToReplay.java
@@ -26,7 +26,7 @@
*/
package org.opends.server.replication.plugin;
-import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
/**
* This is a bag class to hold an update to replay in the queue of updates to
@@ -36,8 +36,8 @@
*/
public class UpdateToReplay
{
- private UpdateMsg updateMessage = null;
- private ReplicationDomain replicationDomain = null;
+ private LDAPUpdateMsg updateMessage = null;
+ private LDAPReplicationDomain replicationDomain = null;
/**
* Construct the object associating the update message with the replication
@@ -46,8 +46,8 @@
* @param replicationDomain The replication domain to use for replaying the
* change from the update message
*/
- public UpdateToReplay(UpdateMsg updateMessage,
- ReplicationDomain replicationDomain)
+ public UpdateToReplay(LDAPUpdateMsg updateMessage,
+ LDAPReplicationDomain replicationDomain)
{
this.updateMessage = updateMessage;
this.replicationDomain = replicationDomain;
@@ -57,7 +57,7 @@
* Getter for update message.
* @return The update message
*/
- public UpdateMsg getUpdateMessage()
+ public LDAPUpdateMsg getUpdateMessage()
{
return updateMessage;
}
@@ -66,7 +66,7 @@
* Getter for replication domain.
* @return The replication domain
*/
- public ReplicationDomain getReplicationDomain()
+ public LDAPReplicationDomain getReplicationDomain()
{
return replicationDomain;
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AckMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AckMsg.java
index 7f5d0e8..3b3f53e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AckMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AckMsg.java
@@ -110,6 +110,42 @@
}
/**
+ * Sets the timeout marker for this message.
+ * @param hasTimeout True if some timeout occurred
+ */
+ public void setHasTimeout(boolean hasTimeout)
+ {
+ this.hasTimeout = hasTimeout;
+ }
+
+ /**
+ * Sets the wrong status marker for this message.
+ * @param hasWrongStatus True if some servers were in wrong status
+ */
+ public void setHasWrongStatus(boolean hasWrongStatus)
+ {
+ this.hasWrongStatus = hasWrongStatus;
+ }
+
+ /**
+ * Sets the replay error marker for this message.
+ * @param hasReplayError True if some servers had errors replaying the change
+ */
+ public void setHasReplayError(boolean hasReplayError)
+ {
+ this.hasReplayError = hasReplayError;
+ }
+
+ /**
+ * Sets the list of failing servers for this message.
+ * @param failedServers The list of failing servers for this message.
+ */
+ public void setFailedServers(List<Short> failedServers)
+ {
+ this.failedServers = failedServers;
+ }
+
+ /**
* Creates a new AckMsg by decoding the provided byte array.
*
* @param in The byte array containing the encoded form of the AckMsg.
@@ -277,4 +313,35 @@
return failedServers;
}
+ /**
+ * Transforms the errors information of the ack into human readable string.
+ * @return A human readable string for errors embedded in the message.
+ */
+ public String errorsToString()
+ {
+ String idList = null;
+ if (failedServers.size() > 0)
+ {
+ idList = "[";
+ int size = failedServers.size();
+ for (int i=0 ; i<size ; i++) {
+ idList += failedServers.get(i);
+ if ( i != (size-1) )
+ idList += ", ";
+ }
+ idList += "]";
+ } else
+ {
+ idList="none";
+ }
+
+ String ackErrorStr = "hasTimeout: " + (hasTimeout ? "yes" : "no") + ", " +
+ "hasWrongStatus: " + (hasWrongStatus ? "yes" : "no") + ", " +
+ "hasReplayError: " + (hasReplayError ? "yes" : "no") + ", " +
+ " concerned server ids: " + idList;
+
+ return ackErrorStr;
+ }
+
}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AddMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
index 29059ad..972a40c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/AddMsg.java
@@ -58,7 +58,7 @@
* This class is used to exchange Add operation between LDAP servers
* and replication servers.
*/
-public class AddMsg extends UpdateMsg
+public class AddMsg extends LDAPUpdateMsg
{
private byte[] encodedAttributes;
private String parentUniqueId;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
index fa8f1c3..df136f2 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
@@ -41,7 +41,7 @@
/**
* Object used when sending delete information to replication servers.
*/
-public class DeleteMsg extends UpdateMsg
+public class DeleteMsg extends LDAPUpdateMsg
{
/**
* Creates a new delete message.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/EntryMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/EntryMsg.java
index 6e54b2e..c05e2ae 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/EntryMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/EntryMsg.java
@@ -43,14 +43,39 @@
/**
* Creates a new EntryMsg.
*
+ * @param sender The sender of this message.
+ * @param destination The destination of this message.
+ * @param entryBytes The bytes of the entry.
+ */
+ public EntryMsg(
+ short sender,
+ short destination,
+ byte[] entryBytes)
+ {
+ super(sender, destination);
+ this.entryByteArray = new byte[entryBytes.length];
+ System.arraycopy(entryBytes, 0, this.entryByteArray, 0, entryBytes.length);
+ }
+
+ /**
+ * Creates a new EntryMsg.
+ *
* @param sender The sender of this message.
* @param destination The destination of this message.
* @param entryBytes The bytes of the entry.
+ * @param pos The starting Position in the array.
+ * @param length Number of array elements to be copied.
*/
- public EntryMsg(short sender, short destination, byte[] entryBytes)
+ public EntryMsg(
+ short sender,
+ short destination,
+ byte[] entryBytes,
+ int pos,
+ int length)
{
super(sender, destination);
- this.entryByteArray = entryBytes.clone();
+ this.entryByteArray = new byte[length];
+ System.arraycopy(entryBytes, pos, this.entryByteArray, 0, length);
}
/**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/HeartbeatMonitor.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/HeartbeatMonitor.java
similarity index 97%
rename from opendj-sdk/opends/src/server/org/opends/server/replication/plugin/HeartbeatMonitor.java
rename to opendj-sdk/opends/src/server/org/opends/server/replication/protocol/HeartbeatMonitor.java
index 08c0ed5..865d97c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/HeartbeatMonitor.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/HeartbeatMonitor.java
@@ -25,7 +25,7 @@
* Copyright 2007-2008 Sun Microsystems, Inc.
*/
-package org.opends.server.replication.plugin;
+package org.opends.server.replication.protocol;
import static org.opends.messages.ReplicationMessages.*;
import static org.opends.server.loggers.ErrorLogger.logError;
@@ -37,7 +37,6 @@
import java.io.IOException;
import org.opends.server.api.DirectoryThread;
-import org.opends.server.replication.protocol.ProtocolSession;
/**
* This class implements a thread to monitor heartbeat messages from the
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/InitializeRequestMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/InitializeRequestMsg.java
index 668bb2d..ebe3adb 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/InitializeRequestMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/InitializeRequestMsg.java
@@ -49,10 +49,10 @@
* @param destination destination of this message
* @param senderID serverID of the server that will send this message
*/
- public InitializeRequestMsg(DN baseDn, short senderID, short destination)
+ public InitializeRequestMsg(String baseDn, short senderID, short destination)
{
super(senderID, destination);
- this.baseDn = baseDn.toNormalizedString();
+ this.baseDn = baseDn;
}
/**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/InitializeTargetMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/InitializeTargetMsg.java
index 70a80e2..194c4ea 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/InitializeTargetMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/InitializeTargetMsg.java
@@ -29,9 +29,6 @@
import java.io.UnsupportedEncodingException;
import java.util.zip.DataFormatException;
-import org.opends.server.types.DN;
-import org.opends.server.types.DirectoryException;
-
/**
* This message is part of the replication protocol.
* This message is sent by a server to one or several servers as the
@@ -59,12 +56,12 @@
* @param requestorID The server that initiates this export.
* @param entryCount The count of entries that will be sent.
*/
- public InitializeTargetMsg(DN baseDN, short senderID,
+ public InitializeTargetMsg(String baseDN, short senderID,
short destination, short requestorID, long entryCount)
{
super(senderID, destination);
this.requestorID = requestorID;
- this.baseDN = baseDN.toNormalizedString();
+ this.baseDN = baseDN;
this.entryCount = entryCount;
}
@@ -144,17 +141,9 @@
*
* @return the base DN
*/
- public DN getBaseDN()
+ public String getBaseDN()
{
- if (baseDN == null)
- return null;
- try
- {
- return DN.decode(baseDN);
- } catch (DirectoryException e)
- {
- return null;
- }
+ return baseDN;
}
/**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
new file mode 100644
index 0000000..c761af5
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
@@ -0,0 +1,492 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2006-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.protocol;
+
+import java.io.UnsupportedEncodingException;
+import java.util.zip.DataFormatException;
+
+import org.opends.server.protocols.asn1.ASN1Exception;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.types.AbstractOperation;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.operation.PostOperationAddOperation;
+import org.opends.server.types.operation.PostOperationDeleteOperation;
+import org.opends.server.types.operation.PostOperationModifyDNOperation;
+import org.opends.server.types.operation.PostOperationModifyOperation;
+import org.opends.server.types.operation.PostOperationOperation;
+
+/**
+ * Abstract class that must be extended to define a message
+ * used for sending Updates between servers.
+ */
+public abstract class LDAPUpdateMsg extends UpdateMsg
+{
+ /**
+ * The DN on which the update was originally done.
+ */
+ protected String dn = null;
+
+ /**
+ * The uniqueId of the entry that was updated.
+ */
+ protected String uniqueId;
+
+ /**
+ * Creates a new UpdateMsg.
+ */
+ public LDAPUpdateMsg()
+ {
+ }
+
+ /**
+ * Creates a new UpdateMsg with the given informations.
+ *
+ * @param ctx The replication Context of the operation for which the
+ * update message must be created,.
+ * @param dn The DN of the entry on which the change
+ * that caused the creation of this object happened
+ */
+ public LDAPUpdateMsg(OperationContext ctx, String dn)
+ {
+ this.protocolVersion = ProtocolVersion.getCurrentVersion();
+ this.changeNumber = ctx.getChangeNumber();
+ this.uniqueId = ctx.getEntryUid();
+ this.dn = dn;
+ }
+
+ /**
+ * Creates a new UpdateMessage with the given informations.
+ *
+ * @param cn The ChangeNumber of the operation for which the
+ * UpdateMessage is created.
+ * @param entryUUID The Unique identifier of the entry that is updated
+ * by the operation for which the UpdateMessage is created.
+ * @param dn The DN of the entry on which the change
+ * that caused the creation of this object happened
+ */
+ public LDAPUpdateMsg(ChangeNumber cn, String entryUUID, String dn)
+ {
+ this.protocolVersion = ProtocolVersion.getCurrentVersion();
+ this.changeNumber = cn;
+ this.uniqueId = entryUUID;
+ this.dn = dn;
+ }
+
+ /**
+ * Generates an Update Message with the provided information.
+ *
+ * @param op The operation for which the message must be created.
+ * @return The generated message.
+ */
+ public static LDAPUpdateMsg generateMsg(PostOperationOperation op)
+ {
+ LDAPUpdateMsg msg = null;
+ switch (op.getOperationType())
+ {
+ case MODIFY :
+ msg = new ModifyMsg((PostOperationModifyOperation) op);
+ break;
+
+ case ADD:
+ msg = new AddMsg((PostOperationAddOperation) op);
+ break;
+
+ case DELETE :
+ msg = new DeleteMsg((PostOperationDeleteOperation) op);
+ break;
+
+ case MODIFY_DN :
+ msg = new ModifyDNMsg( (PostOperationModifyDNOperation) op);
+ break;
+ }
+
+ return msg;
+ }
+
+ /**
+ * Get the DN on which the operation happened.
+ *
+ * @return The DN on which the operations happened.
+ */
+ public String getDn()
+ {
+ return dn;
+ }
+
+ /**
+ * Set the DN.
+ * @param dn The dn that must now be used for this message.
+ */
+ public void setDn(String dn)
+ {
+ this.dn = dn;
+ }
+
+ /**
+ * Get the Unique Identifier of the entry on which the operation happened.
+ *
+ * @return The Unique Identifier of the entry on which the operation happened.
+ */
+ public String getUniqueId()
+ {
+ return uniqueId;
+ }
+
+
+ /**
+ * Create and Operation from the message.
+ *
+ * @param conn connection to use when creating the message
+ * @return the created Operation
+ * @throws LDAPException In case of LDAP decoding exception.
+ * @throws ASN1Exception In case of ASN1 decoding exception.
+ * @throws DataFormatException In case of bad msg format.
+ */
+ public AbstractOperation createOperation(InternalClientConnection conn)
+ throws LDAPException, ASN1Exception, DataFormatException
+ {
+ return createOperation(conn, dn);
+ }
+
+
+ /**
+ * Create and Operation from the message using the provided DN.
+ *
+ * @param conn connection to use when creating the message.
+ * @param newDn the DN to use when creating the operation.
+ * @return the created Operation.
+ * @throws LDAPException In case of LDAP decoding exception.
+ * @throws ASN1Exception In case of ASN1 decoding exception.
+ * @throws DataFormatException In case of bad msg format.
+ */
+ public abstract AbstractOperation createOperation(
+ InternalClientConnection conn, String newDn)
+ throws LDAPException, ASN1Exception, DataFormatException;
+
+ /**
+ * Encode the common header for all the UpdateMsg. This uses the current
+ * protocol version.
+ *
+ * @param type the type of UpdateMsg to encode.
+ * @param additionalLength additional length needed to encode the remaining
+ * part of the UpdateMsg.
+ * @return a byte array containing the common header and enough space to
+ * encode the remaining bytes of the UpdateMsg as was specified
+ * by the additionalLength.
+ * (byte array length = common header length + additionalLength)
+ * @throws UnsupportedEncodingException if UTF-8 is not supported.
+ */
+ public byte[] encodeHeader(byte type, int additionalLength)
+ throws UnsupportedEncodingException
+ {
+ byte[] byteDn = dn.getBytes("UTF-8");
+ byte[] changeNumberByte =
+ this.getChangeNumber().toString().getBytes("UTF-8");
+ byte[] byteEntryuuid = getUniqueId().getBytes("UTF-8");
+
+ /* The message header is stored in the form :
+ * <operation type><protocol version><changenumber><dn><entryuuid><assured>
+ * <assured mode> <safe data level>
+ * the length of result byte array is therefore :
+ * 1 + 1 + change number length + 1 + dn length + 1 + uuid length + 1 + 1
+ * + 1 + 1 + additional_length
+ */
+ int length = 8 + changeNumberByte.length + byteDn.length
+ + byteEntryuuid.length + additionalLength;
+
+ byte[] encodedMsg = new byte[length];
+
+ /* put the type of the operation */
+ encodedMsg[0] = type;
+
+ /* put the protocol version */
+ encodedMsg[1] = (byte)ProtocolVersion.getCurrentVersion();
+ int pos = 2;
+
+ /* Put the ChangeNumber */
+ pos = addByteArray(changeNumberByte, encodedMsg, pos);
+
+ /* Put the DN and a terminating 0 */
+ pos = addByteArray(byteDn, encodedMsg, pos);
+
+ /* Put the entry uuid and a terminating 0 */
+ pos = addByteArray(byteEntryuuid, encodedMsg, pos);
+
+ /* Put the assured flag */
+ encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
+
+ /* Put the assured mode */
+ encodedMsg[pos++] = assuredMode.getValue();
+
+ /* Put the safe data level */
+ encodedMsg[pos++] = safeDataLevel;
+
+ return encodedMsg;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public byte[] getBytes(short reqProtocolVersion)
+ throws UnsupportedEncodingException
+ {
+
+ // Using current protocol version should normally not be done as we would
+ // normally call the getBytes() method instead for that. So this check
+ // for security
+ if (reqProtocolVersion == ProtocolVersion.getCurrentVersion())
+ {
+ return getBytes();
+ }
+
+ switch (reqProtocolVersion)
+ {
+ case ProtocolVersion.REPLICATION_PROTOCOL_V1:
+ return getBytes_V1();
+ default:
+ // Unsupported requested version
+ throw new UnsupportedEncodingException(getClass().getSimpleName() +
+ " PDU does not support requested protocol version serialization: " +
+ reqProtocolVersion);
+ }
+ }
+
+ /**
+ * Get the byte array representation of this Message. This uses the version
+ * 1 of the replication protocol (used for compatibility purpose).
+ *
+ * @return The byte array representation of this Message.
+ *
+ * @throws UnsupportedEncodingException When the encoding of the message
+ * failed because the UTF-8 encoding is not supported.
+ */
+ public abstract byte[] getBytes_V1() throws UnsupportedEncodingException;
+
+ /**
+ * Encode the common header for all the UpdateMessage. This uses the version
+ * 1 of the replication protocol (used for compatibility purpose).
+ *
+ * @param type the type of UpdateMessage to encode.
+ * @param additionalLength additional length needed to encode the remaining
+ * part of the UpdateMessage.
+ * @return a byte array containing the common header and enough space to
+ * encode the remaining bytes of the UpdateMessage as was specified
+ * by the additionalLength.
+ * (byte array length = common header length + additionalLength)
+ * @throws UnsupportedEncodingException if UTF-8 is not supported.
+ */
+ public byte[] encodeHeader_V1(byte type, int additionalLength)
+ throws UnsupportedEncodingException
+ {
+ byte[] byteDn = dn.getBytes("UTF-8");
+ byte[] changeNumberByte =
+ this.getChangeNumber().toString().getBytes("UTF-8");
+ byte[] byteEntryuuid = getUniqueId().getBytes("UTF-8");
+
+ /* The message header is stored in the form :
+ * <operation type><changenumber><dn><assured><entryuuid><change>
+ * the length of result byte array is therefore :
+ * 1 + change number length + 1 + dn length + 1 + 1 +
+ * uuid length + 1 + additional_length
+ */
+ int length = 5 + changeNumberByte.length + byteDn.length
+ + byteEntryuuid.length + additionalLength;
+
+ byte[] encodedMsg = new byte[length];
+
+ /* put the type of the operation */
+ encodedMsg[0] = type;
+ int pos = 1;
+
+ /* put the ChangeNumber */
+ pos = addByteArray(changeNumberByte, encodedMsg, pos);
+
+ /* put the assured information */
+ encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
+
+ /* put the DN and a terminating 0 */
+ pos = addByteArray(byteDn, encodedMsg, pos);
+
+ /* put the entry uuid and a terminating 0 */
+ pos = addByteArray(byteEntryuuid, encodedMsg, pos);
+
+ return encodedMsg;
+ }
+
+ /**
+ * Decode the Header part of this Update Message, and check its type.
+ *
+ * @param types The allowed types of this Update Message.
+ * @param encodedMsg the encoded form of the UpdateMsg.
+ * @return the position at which the remaining part of the message starts.
+ * @throws DataFormatException if the encodedMsg does not contain a valid
+ * common header.
+ */
+ public int decodeHeader(byte[] types, byte[] encodedMsg)
+ throws DataFormatException
+ {
+ /* The message header is stored in the form :
+ * <operation type><protocol version><changenumber><dn><entryuuid><assured>
+ * <assured mode> <safe data level>
+ */
+
+ /* first byte is the type */
+ boolean foundMatchingType = false;
+ for (int i = 0; i < types.length; i++)
+ {
+ if (types[i] == encodedMsg[0])
+ {
+ foundMatchingType = true;
+ break;
+ }
+ }
+ if (!foundMatchingType)
+ throw new DataFormatException("byte[] is not a valid update msg: "
+ + encodedMsg[0]);
+
+ /*
+ * For older protocol version PDUs, decode the matching version header
+ * instead.
+ */
+ if ((encodedMsg[0] == MSG_TYPE_ADD_V1) ||
+ (encodedMsg[0] == MSG_TYPE_DELETE_V1) ||
+ (encodedMsg[0] == MSG_TYPE_MODIFYDN_V1) ||
+ (encodedMsg[0] == MSG_TYPE_MODIFY_V1))
+ {
+ return decodeHeader_V1(encodedMsg);
+ }
+
+ /* read the protocol version */
+ protocolVersion = (short)encodedMsg[1];
+
+ try
+ {
+ /* Read the changeNumber */
+ int pos = 2;
+ int length = getNextLength(encodedMsg, pos);
+ String changenumberStr = new String(encodedMsg, pos, length, "UTF-8");
+ pos += length + 1;
+ changeNumber = new ChangeNumber(changenumberStr);
+
+ /* Read the dn */
+ length = getNextLength(encodedMsg, pos);
+ dn = new String(encodedMsg, pos, length, "UTF-8");
+ pos += length + 1;
+
+ /* Read the entryuuid */
+ length = getNextLength(encodedMsg, pos);
+ uniqueId = new String(encodedMsg, pos, length, "UTF-8");
+ pos += length + 1;
+
+ /* Read the assured information */
+ if (encodedMsg[pos++] == 1)
+ assuredFlag = true;
+ else
+ assuredFlag = false;
+
+ /* Read the assured mode */
+ assuredMode = AssuredMode.valueOf(encodedMsg[pos++]);
+
+ /* Read the safe data level */
+ safeDataLevel = encodedMsg[pos++];
+
+ return pos;
+ } catch (UnsupportedEncodingException e)
+ {
+ throw new DataFormatException("UTF-8 is not supported by this jvm.");
+ } catch (IllegalArgumentException e)
+ {
+ throw new DataFormatException(e.getMessage());
+ }
+ }
+
+ /**
+ * Decode the Header part of this Update Message, and check its type. This
+ * uses the version 1 of the replication protocol (used for compatibility
+ * purpose).
+ *
+ * @param encodedMsg the encoded form of the UpdateMessage.
+ * @return the position at which the remaining part of the message starts.
+ * @throws DataFormatException if the encodedMsg does not contain a valid
+ * common header.
+ */
+ public int decodeHeader_V1(byte[] encodedMsg)
+ throws DataFormatException
+ {
+ if ((encodedMsg[0] != MSG_TYPE_ADD_V1) &&
+ (encodedMsg[0] != MSG_TYPE_DELETE_V1) &&
+ (encodedMsg[0] != MSG_TYPE_MODIFYDN_V1) &&
+ (encodedMsg[0] != MSG_TYPE_MODIFY_V1))
+ throw new DataFormatException("byte[] is not a valid update msg: expected"
+ + " a V1 PDU, received: " + encodedMsg[0]);
+
+ // Force version to V1 (other new parameters take their default values
+ // (assured stuff...))
+ protocolVersion = ProtocolVersion.REPLICATION_PROTOCOL_V1;
+
+ try
+ {
+ /* read the changeNumber */
+ int pos = 1;
+ int length = getNextLength(encodedMsg, pos);
+ String changenumberStr = new String(encodedMsg, pos, length, "UTF-8");
+ pos += length + 1;
+ changeNumber = new ChangeNumber(changenumberStr);
+
+ /* read the assured information */
+ if (encodedMsg[pos++] == 1)
+ assuredFlag = true;
+ else
+ assuredFlag = false;
+
+ /* read the dn */
+ length = getNextLength(encodedMsg, pos);
+ dn = new String(encodedMsg, pos, length, "UTF-8");
+ pos += length + 1;
+
+ /* read the entryuuid */
+ length = getNextLength(encodedMsg, pos);
+ uniqueId = new String(encodedMsg, pos, length, "UTF-8");
+ pos += length + 1;
+
+ return pos;
+ } catch (UnsupportedEncodingException e)
+ {
+ throw new DataFormatException("UTF-8 is not supported by this jvm.");
+ }
+ }
+
+ /**
+ * Return the number of bytes used by this message.
+ *
+ * @return The number of bytes used by this message.
+ */
+ public abstract int size();
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java
index 5d1839a..0339bd7 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ModifyCommonMsg.java
@@ -41,7 +41,7 @@
/**
* This class holds every common code for the modify messages (mod, moddn).
*/
-public abstract class ModifyCommonMsg extends UpdateMsg {
+public abstract class ModifyCommonMsg extends LDAPUpdateMsg {
/**
* The modifications kept encoded in the message.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java
index 5bed97d..f1bdb60 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplServerStartMsg.java
@@ -30,8 +30,6 @@
import java.util.zip.DataFormatException;
import org.opends.server.replication.common.ServerState;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.DN;
/**
* Message sent by a replication server to another replication server
@@ -73,7 +71,7 @@
* @param groupId The group id of the RS
* @param degradedStatusThreshold The degraded status threshold
*/
- public ReplServerStartMsg(short serverId, String serverURL, DN baseDn,
+ public ReplServerStartMsg(short serverId, String serverURL, String baseDn,
int windowSize,
ServerState serverState,
short protocolVersion,
@@ -86,7 +84,7 @@
this.serverId = serverId;
this.serverURL = serverURL;
if (baseDn != null)
- this.baseDn = baseDn.toNormalizedString();
+ this.baseDn = baseDn;
else
this.baseDn = null;
this.windowSize = windowSize;
@@ -202,17 +200,9 @@
*
* @return the base DN from this ReplServerStartMsg.
*/
- public DN getBaseDn()
+ public String getBaseDn()
{
- if (baseDn == null)
- return null;
- try
- {
- return DN.decode(baseDn);
- } catch (DirectoryException e)
- {
- return null;
- }
+ return baseDn;
}
/**
@@ -333,8 +323,8 @@
return "ReplServerStartMsg content: " +
"\nprotocolVersion: " + protocolVersion +
"\ngenerationId: " + generationId +
+ "\nbaseDn: " + baseDn +
"\ngroupId: " + groupId +
- "\nbaseDn: " + baseDn.toString() +
"\nserverId: " + serverId +
"\nserverState: " + serverState +
"\nserverURL: " + serverURL +
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java
index a94d979..778ad65 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java
@@ -31,8 +31,6 @@
import static org.opends.messages.ReplicationMessages.*;
import org.opends.messages.Message;
-import org.opends.server.admin.std.server.ReplicationServerCfg;
-import org.opends.server.admin.std.server.ReplicationDomainCfg;
import org.opends.server.types.DirectoryConfig;
import org.opends.server.types.CryptoManager;
import org.opends.server.config.ConfigException;
@@ -132,32 +130,12 @@
}
/**
- * Create a ReplSessionSecurity instance from a provided replication server
- * configuration.
- *
- * @param replServerCfg The replication server configuration.
- *
- * @throws ConfigException If the supplied configuration was not valid.
- */
- public ReplSessionSecurity(ReplicationServerCfg replServerCfg)
- throws ConfigException
- {
- // Currently use global settings from the crypto manager.
- this(DirectoryConfig.getCryptoManager().getSslCertNickname(),
- DirectoryConfig.getCryptoManager().getSslProtocols(),
- DirectoryConfig.getCryptoManager().getSslCipherSuites(),
- DirectoryConfig.getCryptoManager().isSslEncryption());
- }
-
- /**
* Create a ReplSessionSecurity instance from a provided multimaster domain
* configuration.
*
- * @param multimasterDomainCfg The multimaster domain configuration.
- *
* @throws ConfigException If the supplied configuration was not valid.
*/
- public ReplSessionSecurity(ReplicationDomainCfg multimasterDomainCfg)
+ public ReplSessionSecurity()
throws ConfigException
{
// Currently use global settings from the crypto manager.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java
index 5a0decd..b65af58 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplicationMsg.java
@@ -69,6 +69,7 @@
static final byte MSG_TYPE_TOPOLOGY = 26;
static final byte MSG_TYPE_START_SESSION = 27;
static final byte MSG_TYPE_CHANGE_STATUS = 28;
+ static final byte MSG_TYPE_GENERIC_UPDATE = 29;
// Adding a new type of message here probably requires to
// change accordingly generateMsg method below
@@ -209,6 +210,9 @@
case MSG_TYPE_CHANGE_STATUS:
msg = new ChangeStatusMsg(buffer);
break;
+ case MSG_TYPE_GENERIC_UPDATE:
+ msg = new UpdateMsg(buffer);
+ break;
default:
throw new DataFormatException("received message with unknown type");
}
@@ -224,7 +228,8 @@
* @param pos the position where to concatenate.
* @return the next position to use in the resultByteArray.
*/
- protected int addByteArray(byte[] tail, byte[] resultByteArray, int pos)
+ protected static int addByteArray(byte[] tail, byte[] resultByteArray,
+ int pos)
{
for (int i=0; i<tail.length; i++,pos++)
{
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ServerStartMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ServerStartMsg.java
index 8e8496b..f5679cc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ServerStartMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ServerStartMsg.java
@@ -32,8 +32,6 @@
import java.util.zip.DataFormatException;
import org.opends.server.replication.common.ServerState;
-import org.opends.server.types.DN;
-import org.opends.server.types.DirectoryException;
/**
* This message is used by LDAP server when they first connect.
@@ -85,7 +83,7 @@
* after the start messages have been exchanged.
* @param groupId The group id of the DS for this DN
*/
- public ServerStartMsg(short serverId, DN baseDn, int maxReceiveDelay,
+ public ServerStartMsg(short serverId, String baseDn, int maxReceiveDelay,
int maxReceiveQueue, int maxSendDelay,
int maxSendQueue, int windowSize,
long heartbeatInterval,
@@ -98,7 +96,7 @@
super(protocolVersion, generationId);
this.serverId = serverId;
- this.baseDn = baseDn.toString();
+ this.baseDn = baseDn;
this.maxReceiveDelay = maxReceiveDelay;
this.maxReceiveQueue = maxReceiveQueue;
this.maxSendDelay = maxSendDelay;
@@ -243,15 +241,9 @@
* Get the baseDn.
* @return Returns the baseDn.
*/
- public DN getBaseDn()
+ public String getBaseDn()
{
- try
- {
- return DN.decode(baseDn);
- } catch (DirectoryException e)
- {
- return null;
- }
+ return baseDn;
}
/**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/UpdateMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/UpdateMsg.java
index a2ce835..ca50458 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/UpdateMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/UpdateMsg.java
@@ -26,26 +26,17 @@
*/
package org.opends.server.replication.protocol;
-import java.io.UnsupportedEncodingException;
import java.util.zip.DataFormatException;
-import org.opends.server.protocols.asn1.ASN1Exception;
-import org.opends.server.protocols.internal.InternalClientConnection;
+import java.io.UnsupportedEncodingException;
import org.opends.server.replication.common.AssuredMode;
import org.opends.server.replication.common.ChangeNumber;
-import org.opends.server.types.AbstractOperation;
-import org.opends.server.types.LDAPException;
-import org.opends.server.types.operation.PostOperationAddOperation;
-import org.opends.server.types.operation.PostOperationDeleteOperation;
-import org.opends.server.types.operation.PostOperationModifyDNOperation;
-import org.opends.server.types.operation.PostOperationModifyOperation;
-import org.opends.server.types.operation.PostOperationOperation;
/**
* Abstract class that must be extended to define a message
* used for sending Updates between servers.
*/
-public abstract class UpdateMsg extends ReplicationMsg
+public class UpdateMsg extends ReplicationMsg
implements Comparable<UpdateMsg>
{
/**
@@ -59,11 +50,6 @@
protected ChangeNumber changeNumber;
/**
- * The DN on which the update was originally done.
- */
- protected String dn = null;
-
- /**
* True when the update must use assured replication.
*/
protected boolean assuredFlag = false;
@@ -76,83 +62,62 @@
/**
* When assured mode is safe data, gives the requested level.
*/
- protected byte safeDataLevel = (byte)-1;
+ protected byte safeDataLevel = (byte)1;
/**
- * The uniqueId of the entry that was updated.
+ * The payload that must be encoded in this message.
*/
- protected String uniqueId;
+ private byte[] payload;
+
/**
- * Creates a new UpdateMsg.
+ * Creates a new empty UpdateMsg.
*/
- public UpdateMsg()
+ protected UpdateMsg()
+ {}
+
+ /**
+ * Creates a new UpdateMsg with the given informations.
+ *
+ * @param bytes A Byte Array with the encoded form of the message.
+ *
+ * @throws DataFormatException If bytes is not valid.
+ */
+ UpdateMsg(byte[] bytes) throws DataFormatException
{
+ // Decode header
+ int pos = decodeHeader(MSG_TYPE_GENERIC_UPDATE, bytes);
+
+ /* Read the payload : all the remaining bytes but the terminating 0 */
+ int length = bytes.length - pos;
+ payload = new byte[length];
+ try
+ {
+ System.arraycopy(bytes, pos, payload, 0, length);
+ } catch (IndexOutOfBoundsException e)
+ {
+ throw new DataFormatException(e.getMessage());
+ } catch (ArrayStoreException e)
+ {
+ throw new DataFormatException(e.getMessage());
+ } catch (NullPointerException e)
+ {
+ throw new DataFormatException(e.getMessage());
+ }
}
/**
* Creates a new UpdateMsg with the given informations.
*
- * @param ctx The replication Context of the operation for which the
- * update message must be created,.
- * @param dn The DN of the entry on which the change
- * that caused the creation of this object happened
+ * @param changeNumber The ChangeNumber associated with the change
+ * encoded in this message.
+ * @param payload The payload that must be encoded in this message.
*/
- public UpdateMsg(OperationContext ctx, String dn)
+ public UpdateMsg(ChangeNumber changeNumber, byte[] payload)
{
+ this.payload = payload;
this.protocolVersion = ProtocolVersion.getCurrentVersion();
- this.changeNumber = ctx.getChangeNumber();
- this.uniqueId = ctx.getEntryUid();
- this.dn = dn;
- }
-
- /**
- * Creates a new UpdateMessage with the given informations.
- *
- * @param cn The ChangeNumber of the operation for which the
- * UpdateMessage is created.
- * @param entryUUID The Unique identifier of the entry that is updated
- * by the operation for which the UpdateMessage is created.
- * @param dn The DN of the entry on which the change
- * that caused the creation of this object happened
- */
- public UpdateMsg(ChangeNumber cn, String entryUUID, String dn)
- {
- this.protocolVersion = ProtocolVersion.getCurrentVersion();
- this.changeNumber = cn;
- this.uniqueId = entryUUID;
- this.dn = dn;
- }
-
- /**
- * Generates an Update Message with the provided information.
- *
- * @param op The operation for which the message must be created.
- * @return The generated message.
- */
- public static UpdateMsg generateMsg(PostOperationOperation op)
- {
- UpdateMsg msg = null;
- switch (op.getOperationType())
- {
- case MODIFY :
- msg = new ModifyMsg((PostOperationModifyOperation) op);
- break;
-
- case ADD:
- msg = new AddMsg((PostOperationAddOperation) op);
- break;
-
- case DELETE :
- msg = new DeleteMsg((PostOperationDeleteOperation) op);
- break;
-
- case MODIFY_DN :
- msg = new ModifyDNMsg( (PostOperationModifyDNOperation) op);
- break;
- }
-
- return msg;
+ this.changeNumber = changeNumber;
}
/**
@@ -165,35 +130,6 @@
}
/**
- * Get the DN on which the operation happened.
- *
- * @return The DN on which the operations happened.
- */
- public String getDn()
- {
- return dn;
- }
-
- /**
- * Set the DN.
- * @param dn The dn that must now be used for this message.
- */
- public void setDn(String dn)
- {
- this.dn = dn;
- }
-
- /**
- * Get the Unique Identifier of the entry on which the operation happened.
- *
- * @return The Unique Identifier of the entry on which the operation happened.
- */
- public String getUniqueId()
- {
- return uniqueId;
- }
-
- /**
* Get a boolean indicating if the Update must be processed as an
* Asynchronous or as an assured replication.
*
@@ -250,96 +186,6 @@
return changeNumber.compareTo(msg.getChangeNumber());
}
- /**
- * Create and Operation from the message.
- *
- * @param conn connection to use when creating the message
- * @return the created Operation
- * @throws LDAPException In case of LDAP decoding exception.
- * @throws ASN1Exception In case of ASN1 decoding exception.
- * @throws DataFormatException In case of bad msg format.
- */
- public AbstractOperation createOperation(InternalClientConnection conn)
- throws LDAPException, ASN1Exception, DataFormatException
- {
- return createOperation(conn, dn);
- }
-
-
- /**
- * Create and Operation from the message using the provided DN.
- *
- * @param conn connection to use when creating the message.
- * @param newDn the DN to use when creating the operation.
- * @return the created Operation.
- * @throws LDAPException In case of LDAP decoding exception.
- * @throws ASN1Exception In case of ASN1 decoding exception.
- * @throws DataFormatException In case of bad msg format.
- */
- public abstract AbstractOperation createOperation(
- InternalClientConnection conn, String newDn)
- throws LDAPException, ASN1Exception, DataFormatException;
-
- /**
- * Encode the common header for all the UpdateMsg. This uses the current
- * protocol version.
- *
- * @param type the type of UpdateMsg to encode.
- * @param additionalLength additional length needed to encode the remaining
- * part of the UpdateMsg.
- * @return a byte array containing the common header and enough space to
- * encode the remaining bytes of the UpdateMsg as was specified
- * by the additionalLength.
- * (byte array length = common header length + additionalLength)
- * @throws UnsupportedEncodingException if UTF-8 is not supported.
- */
- public byte[] encodeHeader(byte type, int additionalLength)
- throws UnsupportedEncodingException
- {
- byte[] byteDn = dn.getBytes("UTF-8");
- byte[] changeNumberByte =
- this.getChangeNumber().toString().getBytes("UTF-8");
- byte[] byteEntryuuid = getUniqueId().getBytes("UTF-8");
-
- /* The message header is stored in the form :
- * <operation type><protocol version><changenumber><dn><entryuuid><assured>
- * <assured mode> <safe data level>
- * the length of result byte array is therefore :
- * 1 + 1 + change number length + 1 + dn length + 1 + uuid length + 1 + 1
- * + 1 + 1 + additional_length
- */
- int length = 8 + changeNumberByte.length + byteDn.length
- + byteEntryuuid.length + additionalLength;
-
- byte[] encodedMsg = new byte[length];
-
- /* put the type of the operation */
- encodedMsg[0] = type;
-
- /* put the protocol version */
- encodedMsg[1] = (byte)ProtocolVersion.getCurrentVersion();
- int pos = 2;
-
- /* Put the ChangeNumber */
- pos = addByteArray(changeNumberByte, encodedMsg, pos);
-
- /* Put the DN and a terminating 0 */
- pos = addByteArray(byteDn, encodedMsg, pos);
-
- /* Put the entry uuid and a terminating 0 */
- pos = addByteArray(byteEntryuuid, encodedMsg, pos);
-
- /* Put the assured flag */
- encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
-
- /* Put the assured mode */
- encodedMsg[pos++] = assuredMode.getValue();
-
- /* Put the safe data level */
- encodedMsg[pos++] = safeDataLevel;
-
- return encodedMsg;
- }
/**
* {@inheritDoc}
@@ -353,226 +199,15 @@
{
return getBytes();
}
-
- // Supported older protocol versions
- switch (reqProtocolVersion)
+ else
{
- case ProtocolVersion.REPLICATION_PROTOCOL_V1:
- return getBytes_V1();
- default:
- // Unsupported requested version
- throw new UnsupportedEncodingException(getClass().getSimpleName() +
+ throw new UnsupportedEncodingException(getClass().getSimpleName() +
" PDU does not support requested protocol version serialization: " +
reqProtocolVersion);
}
}
/**
- * Get the byte array representation of this Message. This uses the version
- * 1 of the replication protocol (used for compatibility purpose).
- *
- * @return The byte array representation of this Message.
- *
- * @throws UnsupportedEncodingException When the encoding of the message
- * failed because the UTF-8 encoding is not supported.
- */
- public abstract byte[] getBytes_V1() throws UnsupportedEncodingException;
-
- /**
- * Encode the common header for all the UpdateMessage. This uses the version
- * 1 of the replication protocol (used for compatibility purpose).
- *
- * @param type the type of UpdateMessage to encode.
- * @param additionalLength additional length needed to encode the remaining
- * part of the UpdateMessage.
- * @return a byte array containing the common header and enough space to
- * encode the remaining bytes of the UpdateMessage as was specified
- * by the additionalLength.
- * (byte array length = common header length + additionalLength)
- * @throws UnsupportedEncodingException if UTF-8 is not supported.
- */
- public byte[] encodeHeader_V1(byte type, int additionalLength)
- throws UnsupportedEncodingException
- {
- byte[] byteDn = dn.getBytes("UTF-8");
- byte[] changeNumberByte =
- this.getChangeNumber().toString().getBytes("UTF-8");
- byte[] byteEntryuuid = getUniqueId().getBytes("UTF-8");
-
- /* The message header is stored in the form :
- * <operation type>changenumber><dn><assured><entryuuid><change>
- * the length of result byte array is therefore :
- * 1 + change number length + 1 + dn length + 1 + 1 +
- * uuid length + 1 + additional_length
- */
- int length = 5 + changeNumberByte.length + byteDn.length
- + byteEntryuuid.length + additionalLength;
-
- byte[] encodedMsg = new byte[length];
-
- /* put the type of the operation */
- encodedMsg[0] = type;
- int pos = 1;
-
- /* put the ChangeNumber */
- pos = addByteArray(changeNumberByte, encodedMsg, pos);
-
- /* put the assured information */
- encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
-
- /* put the DN and a terminating 0 */
- pos = addByteArray(byteDn, encodedMsg, pos);
-
- /* put the entry uuid and a terminating 0 */
- pos = addByteArray(byteEntryuuid, encodedMsg, pos);
-
- return encodedMsg;
- }
-
- /**
- * Decode the Header part of this Update Message, and check its type.
- *
- * @param types The allowed types of this Update Message.
- * @param encodedMsg the encoded form of the UpdateMsg.
- * @return the position at which the remaining part of the message starts.
- * @throws DataFormatException if the encodedMsg does not contain a valid
- * common header.
- */
- public int decodeHeader(byte[] types, byte[] encodedMsg)
- throws DataFormatException
- {
- /* The message header is stored in the form :
- * <operation type><protocol version><changenumber><dn><entryuuid><assured>
- * <assured mode> <safe data level>
- */
-
- /* first byte is the type */
- boolean foundMatchingType = false;
- for (int i = 0; i < types.length; i++)
- {
- if (types[i] == encodedMsg[0])
- {
- foundMatchingType = true;
- break;
- }
- }
- if (!foundMatchingType)
- throw new DataFormatException("byte[] is not a valid update msg: "
- + encodedMsg[0]);
-
- /*
- * For older protocol version PDUs, decode the matching version header
- * instead.
- */
- if ((encodedMsg[0] == MSG_TYPE_ADD_V1) ||
- (encodedMsg[0] == MSG_TYPE_DELETE_V1) ||
- (encodedMsg[0] == MSG_TYPE_MODIFYDN_V1) ||
- (encodedMsg[0] == MSG_TYPE_MODIFY_V1))
- {
- return decodeHeader_V1(encodedMsg);
- }
-
- /* read the protocol version */
- protocolVersion = (short)encodedMsg[1];
-
- try
- {
- /* Read the changeNumber */
- int pos = 2;
- int length = getNextLength(encodedMsg, pos);
- String changenumberStr = new String(encodedMsg, pos, length, "UTF-8");
- pos += length + 1;
- changeNumber = new ChangeNumber(changenumberStr);
-
- /* Read the dn */
- length = getNextLength(encodedMsg, pos);
- dn = new String(encodedMsg, pos, length, "UTF-8");
- pos += length + 1;
-
- /* Read the entryuuid */
- length = getNextLength(encodedMsg, pos);
- uniqueId = new String(encodedMsg, pos, length, "UTF-8");
- pos += length + 1;
-
- /* Read the assured information */
- if (encodedMsg[pos++] == 1)
- assuredFlag = true;
- else
- assuredFlag = false;
-
- /* Read the assured mode */
- assuredMode = AssuredMode.valueOf(encodedMsg[pos++]);
-
- /* Read the safe data level */
- safeDataLevel = encodedMsg[pos++];
-
- return pos;
- } catch (UnsupportedEncodingException e)
- {
- throw new DataFormatException("UTF-8 is not supported by this jvm.");
- } catch (IllegalArgumentException e)
- {
- throw new DataFormatException(e.getMessage());
- }
- }
-
- /**
- * Decode the Header part of this Update Message, and check its type. This
- * uses the version 1 of the replication protocol (used for compatibility
- * purpose).
- *
- * @param encodedMsg the encoded form of the UpdateMessage.
- * @return the position at which the remaining part of the message starts.
- * @throws DataFormatException if the encodedMsg does not contain a valid
- * common header.
- */
- public int decodeHeader_V1(byte[] encodedMsg)
- throws DataFormatException
- {
- if ((encodedMsg[0] != MSG_TYPE_ADD_V1) &&
- (encodedMsg[0] != MSG_TYPE_DELETE_V1) &&
- (encodedMsg[0] != MSG_TYPE_MODIFYDN_V1) &&
- (encodedMsg[0] != MSG_TYPE_MODIFY_V1))
- throw new DataFormatException("byte[] is not a valid update msg: expected"
- + " a V1 PDU, received: " + encodedMsg[0]);
-
- // Force version to V1 (other new parameters take their default values
- // (assured stuff...))
- protocolVersion = ProtocolVersion.REPLICATION_PROTOCOL_V1;
-
- try
- {
- /* read the changeNumber */
- int pos = 1;
- int length = getNextLength(encodedMsg, pos);
- String changenumberStr = new String(encodedMsg, pos, length, "UTF-8");
- pos += length + 1;
- changeNumber = new ChangeNumber(changenumberStr);
-
- /* read the assured information */
- if (encodedMsg[pos++] == 1)
- assuredFlag = true;
- else
- assuredFlag = false;
-
- /* read the dn */
- length = getNextLength(encodedMsg, pos);
- dn = new String(encodedMsg, pos, length, "UTF-8");
- pos += length + 1;
-
- /* read the entryuuid */
- length = getNextLength(encodedMsg, pos);
- uniqueId = new String(encodedMsg, pos, length, "UTF-8");
- pos += length + 1;
-
- return pos;
- } catch (UnsupportedEncodingException e)
- {
- throw new DataFormatException("UTF-8 is not supported by this jvm.");
- }
- }
-
- /**
* Get the assured mode in this message.
* @return The assured mode in this message
*/
@@ -626,5 +261,144 @@
*
* @return The number of bytes used by this message.
*/
- public abstract int size();
+ public int size()
+ {
+ return 10 + payload.length;
+ }
+
+ /**
+ * Encode the common header for all the UpdateMsg. This uses the current
+ * protocol version.
+ *
+ * @param type the type of UpdateMsg to encode.
+ * @param additionalLength additional length needed to encode the remaining
+ * part of the UpdateMsg.
+ * @return a byte array containing the common header and enough space to
+ * encode the remaining bytes of the UpdateMsg as was specified
+ * by the additionalLength.
+ * (byte array length = common header length + additionalLength)
+ * @throws UnsupportedEncodingException if UTF-8 is not supported.
+ */
+ protected byte[] encodeHeader(byte type, int additionalLength)
+ throws UnsupportedEncodingException
+ {
+ byte[] changeNumberByte =
+ this.getChangeNumber().toString().getBytes("UTF-8");
+
+ /* The message header is stored in the form :
+ * <operation type><protocol version><changenumber><assured>
+ * <assured mode> <safe data level>
+ * the length of result byte array is therefore :
+ * 1 + 1 + change number length + 1 + 1
+ * + 1 + 1 + additional_length
+ */
+ int length = 6 + changeNumberByte.length + additionalLength;
+
+ byte[] encodedMsg = new byte[length];
+
+ /* put the type of the operation */
+ encodedMsg[0] = type;
+
+ /* put the protocol version */
+ encodedMsg[1] = (byte)ProtocolVersion.getCurrentVersion();
+ int pos = 2;
+
+ /* Put the ChangeNumber */
+ pos = addByteArray(changeNumberByte, encodedMsg, pos);
+
+ /* Put the assured flag */
+ encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
+
+ /* Put the assured mode */
+ encodedMsg[pos++] = assuredMode.getValue();
+
+ /* Put the safe data level */
+ encodedMsg[pos++] = safeDataLevel;
+
+ return encodedMsg;
+ }
+
+ /**
+ * Decode the Header part of this Update Message, and check its type.
+ *
+ * @param type The allowed type of this Update Message.
+ * @param encodedMsg the encoded form of the UpdateMsg.
+ * @return the position at which the remaining part of the message starts.
+ * @throws DataFormatException if the encodedMsg does not contain a valid
+ * common header.
+ */
+ protected int decodeHeader(byte type, byte[] encodedMsg)
+ throws DataFormatException
+ {
+ /* The message header is stored in the form :
+ * <operation type><protocol version><changenumber><assured>
+ * <assured mode> <safe data level>
+ */
+ if (!(type == encodedMsg[0]))
+ throw new DataFormatException("byte[] is not a valid update msg: "
+ + encodedMsg[0]);
+
+ /* read the protocol version */
+ protocolVersion = (short)encodedMsg[1];
+
+ try
+ {
+ /* Read the changeNumber */
+ int pos = 2;
+ int length = getNextLength(encodedMsg, pos);
+ String changenumberStr = new String(encodedMsg, pos, length, "UTF-8");
+ pos += length + 1;
+ changeNumber = new ChangeNumber(changenumberStr);
+
+ /* Read the assured information */
+ if (encodedMsg[pos++] == 1)
+ assuredFlag = true;
+ else
+ assuredFlag = false;
+
+ /* Read the assured mode */
+ assuredMode = AssuredMode.valueOf(encodedMsg[pos++]);
+
+ /* Read the safe data level */
+ safeDataLevel = encodedMsg[pos++];
+
+ return pos;
+ } catch (UnsupportedEncodingException e)
+ {
+ throw new DataFormatException("UTF-8 is not supported by this jvm.");
+ } catch (IllegalArgumentException e)
+ {
+ throw new DataFormatException(e.getMessage());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public byte[] getBytes() throws UnsupportedEncodingException
+ {
+ /* Encode the header in a byte[] large enough to also contain the payload */
+ byte [] resultByteArray =
+ encodeHeader(MSG_TYPE_GENERIC_UPDATE, payload.length);
+
+ int pos = resultByteArray.length - payload.length;
+
+ /* Add the payload */
+ for (int i=0; i<payload.length; i++,pos++)
+ {
+ resultByteArray[pos] = payload[i];
+ }
+ return resultByteArray;
+ }
+
+ /**
+ * Get the payload of the UpdateMsg.
+ *
+ * @return The payload of the UpdateMsg.
+ */
+ public byte[] getPayload()
+ {
+ return payload;
+ }
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/WindowMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/WindowMsg.java
index 7407f1c..57cbac0 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/WindowMsg.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/WindowMsg.java
@@ -137,7 +137,6 @@
@Override
public String toString()
{
- return "ServerStartMsg content: " +
- "\nnumAck: " + numAck;
+ return "WindowMsg : " + "numAck: " + numAck;
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/AckMessageList.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/AckMessageList.java
deleted file mode 100644
index f085294..0000000
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/AckMessageList.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- * Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- * Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package org.opends.server.replication.server;
-
-import java.util.LinkedList;
-
-import org.opends.server.replication.common.ChangeNumber;
-
-/**
- * This class is used to store the list of acks received for
- * a Given Update Messages.
- *
- * The acks are kept only for the update that are marked, hopefully this
- * should be a limited number of updates and in all cases, LDAP servers
- * operations are going to be blocked waiting for these acks so they
- * won't be able to generate a huge number of such messages.
- *
- * Therefore, the amount of memory used keeping those changes is not a problem,
- */
-public class AckMessageList
-{
- // The ChangeNumber of the updates that was acked
- // or that is waiting acks
- private ChangeNumber changeNumber;
-
- // The list of serverIdentifiers for which acks have been received so far
- // can be empty when no acks have been received
- private LinkedList<Short> acks;
-
- private int numExpectedAcks;
-
- /**
- * Creates a new AckMessageList for a given ChangeNumber.
- *
- * @param changeNumber The ChangeNumber for which the ack list is created.
- * @param numExpectedAcks The number of acks waited before acking the
- * original change.
- */
- public AckMessageList(ChangeNumber changeNumber, int numExpectedAcks)
- {
- acks = new LinkedList<Short>();
- this.changeNumber = changeNumber;
- this.numExpectedAcks = numExpectedAcks;
- }
-
- /**
- * Get the ChangeNumber of this Ack Message List.
- * @return Returns the changeNumber.
- */
- public ChangeNumber getChangeNumber()
- {
- return changeNumber;
- }
-
- /**
- * Add an ack from a given LDAP server to the ack list.
- *
- * @param serverId the identifier of the LDAP server.
- */
- public synchronized void addAck(short serverId)
- {
- acks.add(serverId);
- }
-
- /**
- * This method can be used to check if all acks have been received for the
- * ChangeNumber managed by this list.
- * @return A boolean indicating if all acks have been received for the
- * ChangeNumber managed by this list.
- */
- public boolean completed()
- {
- return (acks.size() >= numExpectedAcks);
- }
-}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/DbHandler.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/DbHandler.java
index b16aa28..097638d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/DbHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/DbHandler.java
@@ -43,7 +43,6 @@
import org.opends.server.config.ConfigException;
import org.opends.server.types.Attribute;
import org.opends.server.types.Attributes;
-import org.opends.server.types.DN;
import org.opends.server.types.InitializationException;
import org.opends.server.util.TimeThread;
import org.opends.server.core.DirectoryServer;
@@ -102,7 +101,7 @@
private ChangeNumber firstChange = null;
private ChangeNumber lastChange = null;
private short serverId;
- private DN baseDn;
+ private String baseDn;
private DbMonitorProvider dbMonitor = new DbMonitorProvider();
private boolean shutdown = false;
private boolean done = false;
@@ -133,7 +132,7 @@
* @throws DatabaseException If a database problem happened
*/
public DbHandler(
- short id, DN baseDn, ReplicationServer replicationServer,
+ short id, String baseDn, ReplicationServer replicationServer,
ReplicationDbEnv dbenv, int queueSize)
throws DatabaseException
{
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ExpectedAcksInfo.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ExpectedAcksInfo.java
new file mode 100644
index 0000000..296e499
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ExpectedAcksInfo.java
@@ -0,0 +1,141 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.replication.server;
+
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.protocol.AckMsg;
+
+/**
+ * This class is the mother class for sub-classes holding any information needed
+ * about the acks that the replication server will wait for, when he receives an
+ * update message with the assured flag on (assured replication acknowledgments
+ * expected).
+ * It also includes info/routines for constructing the final ack to be sent to
+ * the sender of the update message.
+ *
+ * It is expected to have one sub-class per assured replication sub mode.
+ */
+public abstract class ExpectedAcksInfo
+{
+ // The server handler of the server that sent the assured update message and
+ // to whow we want to return the final ack
+ private ServerHandler requesterServerHandler = null;
+
+ // The requested assured mode of matcching update message
+ private AssuredMode assuredMode = null;
+
+ /**
+ * The change number of the assured update message we want acks for.
+ */
+ protected ChangeNumber changeNumber = null;
+
+ /**
+ * Is the treatment of the acks for the update message completed or not ?
+ * This is used for concurrent access to this object by either the assured
+ * timeout task or the code for processing an ack for the matching update
+ * message. This should be set to true when the treatment of the expected
+ * acks is completed or an ack timeout has occured and we are going to remove
+ * this object from the map where it is stored.
+ */
+ private boolean completed = false;
+
+ /**
+ * Creates a new ExpectedAcksInfo.
+ * @param changeNumber The change number of the assured update message
+ * @param requesterServerHandler The server handler of the server that sent
+ * the assured update message
+ * @param assuredMode The assured mode requested by the assured update message
+ */
+ protected ExpectedAcksInfo(ChangeNumber changeNumber,
+ ServerHandler requesterServerHandler, AssuredMode assuredMode)
+ {
+ this.requesterServerHandler = requesterServerHandler;
+ this.assuredMode = assuredMode;
+ this.changeNumber = changeNumber;
+ }
+
+ /**
+ * Gets the server handler of the server which requested the acknowledgments.
+ * @return The server handler of the server which requested the
+ * acknowledgments.
+ */
+ public ServerHandler getRequesterServer()
+ {
+ return requesterServerHandler;
+ }
+
+ /**
+ * Gets the requested assured mode for the matching update message.
+ * @return The requested assured mode for the matching update message.
+ */
+ public AssuredMode getAssuredMode()
+ {
+ return assuredMode;
+ }
+
+ /**
+ * Process the received ack from a server we are waiting an ack from.
+ * @param ackingServer The server handler of the server that sent the ack
+ * @param ackMsg The ack message to process
+ * @return True if the expected number of acks has just been reached
+ */
+ public abstract boolean processReceivedAck(ServerHandler ackingServer,
+ AckMsg ackMsg);
+
+ /**
+ * Creates the ack message to be returned to the requester server, taking into
+ * account the information in the received acks from every servers.
+ * @param timeout True if we call this method when the timeout occurred, that
+ * is we did not received every expected acks in time, and thus, the timeout
+ * flag should also be enabled in the returned ack message.
+ * @return The ack message ready to be sent to the requester server
+ */
+ public abstract AckMsg createAck(boolean timeout);
+
+ /**
+ * Has the treatment of this object been completed or not?
+ * If true is returned, one must not modify this object (useless) nor remove
+ * it from the map where it is stored (will be or has already been done by the
+ * other code (ack timeout code, or ack processing code)).
+ * @return True if treatment of this object has been completed.
+ */
+ public boolean isCompleted()
+ {
+ return completed;
+ }
+
+ /**
+ * Signal that treatment of this object has been completed and that it is
+ * going to be removed from the map where it is stored.
+ */
+ public void completed()
+ {
+ completed = true;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
index 19a4671..ccbf43f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/LightweightServerHandler.java
@@ -254,7 +254,7 @@
attributes.add(Attributes.create("server-id",
String.valueOf(serverId)));
attributes.add(Attributes.create("base-dn",
- rsDomain.getBaseDn().toNormalizedString()));
+ rsDomain.getBaseDn()));
attributes.add(Attributes.create("connected-to",
replServerHandler.getMonitorInstanceName()));
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/MonitorData.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/MonitorData.java
index 0801257..25b9992 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/MonitorData.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/MonitorData.java
@@ -69,6 +69,10 @@
private ConcurrentHashMap<Short, ServerState> LDAPStates =
new ConcurrentHashMap<Short, ServerState>();
+ // A Map containing the ServerStates of each RS.
+ private ConcurrentHashMap<Short, ServerState> RSStates =
+ new ConcurrentHashMap<Short, ServerState>();
+
// For each LDAP server, the last(max) CN it published
private ConcurrentHashMap<Short, ChangeNumber> maxCNs =
new ConcurrentHashMap<Short, ChangeNumber>();
@@ -86,6 +90,9 @@
private ConcurrentHashMap<Short, Long> fmRSDate =
new ConcurrentHashMap<Short, Long>();
+ private ConcurrentHashMap<Short, Long> missingChangesRS =
+ new ConcurrentHashMap<Short, Long>();
+
/**
* Get an approximation of the latency delay of the replication.
@@ -129,13 +136,29 @@
}
/**
+ * Get the number of missing changes for a Replication Server.
+ *
+ * @param serverId The server ID.
+ *
+ * @return The number of missing changes.
+ */
+ public long getMissingChangesRS(short serverId)
+ {
+ Long res = missingChangesRS.get(serverId);
+ if (res==null)
+ return 0;
+ else
+ return res;
+ }
+
+ /**
* Build the monitor data that are computed from the collected ones.
*/
public void completeComputing()
{
String mds = "";
- // Computes the missing changes counters
+ // Computes the missing changes counters for LDAP servers
// For each LSi ,
// Regarding each other LSj
// Sum the difference : max(LSj) - state(LSi)
@@ -167,6 +190,36 @@
}
mds += "=" + lsiMissingChanges;
this.missingChanges.put(lsiSid,lsiMissingChanges);
+ }
+
+ // Computes the missing changes counters for RS :
+ // Sum the difference of seqnuence numbers for each element in the States.
+
+ for (short lsiSid : RSStates.keySet())
+ {
+ ServerState lsiState = this.RSStates.get(lsiSid);
+ Long lsiMissingChanges = (long)0;
+ if (lsiState != null)
+ {
+ Iterator<Short> lsjMaxItr = this.maxCNs.keySet().iterator();
+ while (lsjMaxItr.hasNext())
+ {
+ Short lsjSid = lsjMaxItr.next();
+ ChangeNumber lsjMaxCN = this.maxCNs.get(lsjSid);
+ ChangeNumber lsiLastCN = lsiState.getMaxChangeNumber(lsjSid);
+
+ int missingChangesLsiLsj =
+ ChangeNumber.diffSeqNum(lsjMaxCN, lsiLastCN);
+
+ mds +=
+ "+ diff("+lsjMaxCN+"-"
+ +lsiLastCN+")="+missingChangesLsiLsj;
+
+ lsiMissingChanges += missingChangesLsiLsj;
+ }
+ }
+ mds += "=" + lsiMissingChanges;
+ this.missingChangesRS.put(lsiSid,lsiMissingChanges);
if (debugEnabled())
TRACER.debugInfo(
@@ -307,6 +360,17 @@
}
/**
+ * Set the state of the RS with the provided serverId.
+ *
+ * @param serverId The server ID.
+ * @param state The server state.
+ */
+ public void setRSState(short serverId, ServerState state)
+ {
+ RSStates.put(serverId, state);
+ }
+
+ /**
* Set the state of the LDAP server with the provided serverId.
* @param serverId The server ID.
* @param newFmd The first missing date.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/NotAssuredUpdateMsg.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/NotAssuredUpdateMsg.java
new file mode 100644
index 0000000..961c8f1
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/NotAssuredUpdateMsg.java
@@ -0,0 +1,358 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.server;
+
+import java.io.UnsupportedEncodingException;
+import java.util.zip.DataFormatException;
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
+import org.opends.server.replication.protocol.ProtocolVersion;
+
+/**
+ * This is a facility class that is in fact an hack to optimize replication
+ * server performances in case of assured replication usage:
+ * When received from a server by a server reader, an update message is to be
+ * posted in the queues of the server writers. Thus, they receive the same
+ * reference of update message. As we want to transform an assured update
+ * message to an equivalent not assured one for some servers but not for all,
+ * instead of performing a painful clone of the message, we use this special
+ * class to keep a reference to the real object, but that will overwrite the
+ * assured flag value to false when serializing the message, and return false
+ * when calling the isAssured() method.
+ *
+ */
+public class NotAssuredUpdateMsg extends UpdateMsg
+{
+ // The real update message this message represents
+ private UpdateMsg realUpdateMsg = null;
+
+ /**
+ * Creates a new empty UpdateMsg.
+ * This class is only used by replication server code so constructor is not
+ * public by security.
+ * @param updateMsg The real underlying update message this object represents.
+ */
+ NotAssuredUpdateMsg(UpdateMsg updateMsg)
+ {
+ realUpdateMsg = updateMsg;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ChangeNumber getChangeNumber()
+ {
+ return realUpdateMsg.getChangeNumber();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAssured()
+ {
+ // Always return false as we represent a not assured message
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setAssured(boolean assured)
+ {
+ // No impact for this method as semantic is that assured is always false
+ // and we do not want to change the original real update message settings
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ // Compare with the underlying real update message
+ if (obj != null)
+ {
+ if (obj.getClass() != realUpdateMsg.getClass())
+ return false;
+ return realUpdateMsg.getChangeNumber().
+ equals(((UpdateMsg)obj).getChangeNumber());
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode()
+ {
+ return realUpdateMsg.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int compareTo(UpdateMsg msg)
+ {
+ return realUpdateMsg.compareTo(msg);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public byte[] getBytes(short reqProtocolVersion)
+ throws UnsupportedEncodingException
+ {
+ // Get the encoding of the real message then overwrite the assured flag byte
+ // to always be false
+ byte[] bytes = realUpdateMsg.getBytes(reqProtocolVersion);
+ int maxLen = bytes.length;
+ int pos = -1;
+ int nZeroFound = 0; // Number of 0 value found
+ boolean found = false;
+
+ /**
+ * Overwrite the assured flag at the right position according to
+ * message type.
+ */
+ // Look for assured flag position
+ if (realUpdateMsg instanceof LDAPUpdateMsg)
+ {
+ // LDAP update message
+ switch (reqProtocolVersion)
+ {
+ case ProtocolVersion.REPLICATION_PROTOCOL_V1:
+ /* The message header is stored in the form :
+ * <operation type><changenumber><dn><assured><entryuuid><change>
+ * the length of result byte array is therefore :
+ * 1 + change number length + 1 + dn length + 1 + 1 +
+ * uuid length + 1 + additional_length
+ * See LDAPUpdateMsg.encodeHeader_V1() for more information
+ */
+ // Find end of change number then end of dn
+ for (pos = 1 ; pos < maxLen ; pos++ ) {
+ if (bytes[pos] == (byte)0)
+ {
+ nZeroFound++;
+ if (nZeroFound == 2) // 2 end of string to find
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found)
+ throw new UnsupportedEncodingException("Could not find end of " +
+ "change number.");
+ pos++;
+ if (pos >= maxLen)
+ throw new UnsupportedEncodingException("Reached end of packet.");
+ // Force assured flag to false
+ bytes[pos] = (byte)0;
+ break;
+ case ProtocolVersion.REPLICATION_PROTOCOL_V2:
+ /* The message header is stored in the form :
+ * <operation type><protocol version><changenumber><dn><entryuuid>
+ * <assured> <assured mode> <safe data level>
+ * the length of result byte array is therefore :
+ * 1 + 1 + change number length + 1 + dn length + 1 + uuid length +
+ * 1 + 1 + 1 + 1 + additional_length
+ * See LDAPUpdateMsg.encodeHeader() for more information
+ */
+ // Find end of change number then end of dn then end of uuid
+ for (pos = 2 ; pos < maxLen ; pos++ ) {
+ if (bytes[pos] == (byte)0)
+ {
+ nZeroFound++;
+ if (nZeroFound == 3) // 3 end of string to find
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found)
+ throw new UnsupportedEncodingException("Could not find end of " +
+ "change number.");
+ pos++;
+ if (pos >= maxLen)
+ throw new UnsupportedEncodingException("Reached end of packet.");
+ // Force assured flag to false
+ bytes[pos] = (byte)0;
+ break;
+ default:
+ throw new UnsupportedEncodingException("Unsupported requested " +
+ " protocol version: " + reqProtocolVersion);
+ }
+ } else
+ {
+ if (!(realUpdateMsg instanceof UpdateMsg))
+ {
+ // Should never happen
+ throw new UnsupportedEncodingException(
+ "Unknown underlying real message type.");
+ }
+ // This is a generic update message
+ /* The message header is stored in the form :
+ * <operation type><protocol version><changenumber><assured>
+ * <assured mode> <safe data level>
+ * the length of result byte array is therefore :
+ * 1 + 1 + change number length + 1 + 1
+ * + 1 + 1 + additional_length
+ * See UpdateMsg.encodeHeader() for more information
+ */
+ // Find end of change number
+ for (pos = 2 ; pos < maxLen ; pos++ )
+ {
+ if (bytes[pos] == (byte)0)
+ {
+ nZeroFound++;
+ if (nZeroFound == 1) // 1 end of string to find
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found)
+ throw new UnsupportedEncodingException("Could not find end of " +
+ "change number.");
+ pos++;
+ if (pos >= maxLen)
+ throw new UnsupportedEncodingException("Reached end of packet.");
+ // Force assured flag to false
+ bytes[pos] = (byte) 0;
+ }
+ return bytes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public byte[] getBytes() throws UnsupportedEncodingException
+ {
+ return getBytes(ProtocolVersion.getCurrentVersion());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AssuredMode getAssuredMode()
+ {
+ return realUpdateMsg.getAssuredMode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public byte getSafeDataLevel()
+ {
+ return realUpdateMsg.getSafeDataLevel();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setAssuredMode(AssuredMode assuredMode)
+ {
+ // No impact for this method as semantic is that assured is always false
+ // and we do not want to change the original real update message settings
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setSafeDataLevel(byte safeDataLevel)
+ {
+ // No impact for this method as semantic is that assured is always false
+ // and we do not want to change the original real update message settings
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public short getVersion()
+ {
+ return realUpdateMsg.getVersion();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int size()
+ {
+ return realUpdateMsg.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected byte[] encodeHeader(byte type, int additionalLength)
+ throws UnsupportedEncodingException
+ {
+ // No called as only used by constructors using bytes
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int decodeHeader(byte type, byte[] encodedMsg)
+ throws DataFormatException
+ {
+ // No called as only used by getBytes methods
+ return -1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public byte[] getPayload()
+ {
+ return realUpdateMsg.getPayload();
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplServerAckMessageList.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplServerAckMessageList.java
deleted file mode 100644
index 8f4ab2a..0000000
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplServerAckMessageList.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- * Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- * Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package org.opends.server.replication.server;
-
-import org.opends.server.replication.common.ChangeNumber;
-
-/**
- * This class is used to store acks for update messages coming from
- * other replication servers.
- */
-public class ReplServerAckMessageList extends AckMessageList
-{
- private short replicationServerId;
- private ReplicationServerDomain replicationServerDomain;
-
- /**
- * Creates a new AckMessageList for a given ChangeNumber.
- *
- * @param changeNumber The ChangeNumber for which the ack list is created.
- * @param numExpectedAcks The number of acks waited before acking the
- * original change.
- * @param replicationServerId The Identifier of the replication server
- * from which the change was received.
- * @param replicationServerDomain The ReplicationServerDomain from which he
- * change was received.
- */
- public ReplServerAckMessageList(ChangeNumber changeNumber,
- int numExpectedAcks,
- short replicationServerId,
- ReplicationServerDomain replicationServerDomain)
- {
- super(changeNumber, numExpectedAcks);
- this.replicationServerId = replicationServerId;
- this.replicationServerDomain = replicationServerDomain;
- }
-
- /**
- * Get the Identifier of the replication server from which we received the
- * change.
- * @return Returns the Identifier of the replication server from which we
- * received the change.
- */
- public short getReplicationServerId()
- {
- return replicationServerId;
- }
-
- /**
- * Get the replicationServerDomain of the replication server from which we
- * received the change.
- * @return Returns the replicationServerDomain of the replication server from
- * which we received the change .
- */
- public ReplicationServerDomain getChangelogCache()
- {
- return replicationServerDomain;
- }
-
-
-}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
index c56863a..053d3aa 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
@@ -34,6 +34,8 @@
import static org.opends.server.loggers.debug.DebugLogger.getTracer;
import static org.opends.server.util.StaticUtils.*;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
+
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -858,7 +860,7 @@
/**
* Export one change.
*/
- private void processChange(UpdateMsg msg,
+ private void processChange(UpdateMsg updateMsg,
LDIFExportConfig exportConfig, LDIFWriter ldifWriter,
SearchOperation searchOperation, String baseDN)
{
@@ -873,159 +875,165 @@
try
{
- if (msg instanceof AddMsg)
+ if (updateMsg instanceof LDAPUpdateMsg)
{
- AddMsg addMsg = (AddMsg)msg;
- AddOperation addOperation = (AddOperation)msg.createOperation(conn);
+ LDAPUpdateMsg msg = (LDAPUpdateMsg) updateMsg;
- dn = DN.decode("puid=" + addMsg.getParentUid() + "+" +
- CHANGE_NUMBER + "=" + msg.getChangeNumber().toString() + "+" +
- msg.getDn() + "," + BASE_DN);
-
- Map<AttributeType,List<Attribute>> attributes =
- new HashMap<AttributeType,List<Attribute>>();
- Map<ObjectClass, String> objectclasses =
- new HashMap<ObjectClass, String>();
-
- for (RawAttribute a : addOperation.getRawAttributes())
+ if (msg instanceof AddMsg)
{
- Attribute attr = a.toAttribute();
- if (attr.getAttributeType().isObjectClassType())
- {
- for (ByteString os : a.getValues())
- {
- String ocName = os.toString();
- ObjectClass oc =
- DirectoryServer.getObjectClass(toLowerCase(ocName));
- if (oc == null)
- {
- oc = DirectoryServer.getDefaultObjectClass(ocName);
- }
+ AddMsg addMsg = (AddMsg)msg;
+ AddOperation addOperation = (AddOperation)msg.createOperation(conn);
- objectclasses.put(oc,ocName);
+ dn = DN.decode("puid=" + addMsg.getParentUid() + "+" +
+ CHANGE_NUMBER + "=" + msg.getChangeNumber().toString() + "+" +
+ msg.getDn() + "," + BASE_DN);
+
+ Map<AttributeType,List<Attribute>> attributes =
+ new HashMap<AttributeType,List<Attribute>>();
+ Map<ObjectClass, String> objectclasses =
+ new HashMap<ObjectClass, String>();
+
+ for (RawAttribute a : addOperation.getRawAttributes())
+ {
+ Attribute attr = a.toAttribute();
+ if (attr.getAttributeType().isObjectClassType())
+ {
+ for (ByteString os : a.getValues())
+ {
+ String ocName = os.toString();
+ ObjectClass oc =
+ DirectoryServer.getObjectClass(toLowerCase(ocName));
+ if (oc == null)
+ {
+ oc = DirectoryServer.getDefaultObjectClass(ocName);
+ }
+
+ objectclasses.put(oc,ocName);
+ }
}
+ else
+ {
+ addAttribute(attributes, attr);
+ }
+ }
+
+ Attribute changetype = Attributes.create("changetype", "add");
+ addAttribute(attributes, changetype);
+
+ if (exportConfig != null)
+ {
+ AddChangeRecordEntry changeRecord =
+ new AddChangeRecordEntry(dn, attributes);
+ ldifWriter.writeChangeRecord(changeRecord);
}
else
{
- addAttribute(attributes, attr);
+ entry = new Entry(dn, objectclasses, attributes, null);
+ }
+ }
+ else if (msg instanceof DeleteMsg)
+ {
+ DeleteMsg delMsg = (DeleteMsg)msg;
+
+ dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
+ CHANGE_NUMBER + "=" + delMsg.getChangeNumber().toString()+ "," +
+ msg.getDn() +","+ BASE_DN);
+
+ DeleteChangeRecordEntry changeRecord =
+ new DeleteChangeRecordEntry(dn);
+ if (exportConfig != null)
+ {
+ ldifWriter.writeChangeRecord(changeRecord);
+ }
+ else
+ {
+ Writer writer = new Writer();
+ LDIFWriter ldifWriter2 = writer.getLDIFWriter();
+ ldifWriter2.writeChangeRecord(changeRecord);
+ LDIFReader reader = writer.getLDIFReader();
+ entry = reader.readEntry();
+ }
+ }
+ else if (msg instanceof ModifyMsg)
+ {
+ ModifyOperation op = (ModifyOperation)msg.createOperation(conn);
+
+ dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
+ CHANGE_NUMBER + "=" + msg.getChangeNumber().toString()+ "," +
+ msg.getDn() +","+ BASE_DN);
+ op.setInternalOperation(true);
+
+ ModifyChangeRecordEntry changeRecord =
+ new ModifyChangeRecordEntry(dn, op.getRawModifications());
+ if (exportConfig != null)
+ {
+ ldifWriter.writeChangeRecord(changeRecord);
+ }
+ else
+ {
+ Writer writer = new Writer();
+ LDIFWriter ldifWriter2 = writer.getLDIFWriter();
+ ldifWriter2.writeChangeRecord(changeRecord);
+ LDIFReader reader = writer.getLDIFReader();
+ entry = reader.readEntry();
+ }
+ }
+ else if (msg instanceof ModifyDNMsg)
+ {
+ ModifyDNOperation op = (ModifyDNOperation)msg.createOperation(conn);
+
+ dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
+ CHANGE_NUMBER + "=" + msg.getChangeNumber().toString()+ "," +
+ msg.getDn() +","+ BASE_DN);
+ op.setInternalOperation(true);
+
+ ModifyDNChangeRecordEntry changeRecord =
+ new ModifyDNChangeRecordEntry(dn, op.getNewRDN(), op.deleteOldRDN(),
+ op.getNewSuperior());
+
+ if (exportConfig != null)
+ {
+ ldifWriter.writeChangeRecord(changeRecord);
+ }
+ else
+ {
+ Writer writer = new Writer();
+ LDIFWriter ldifWriter2 = writer.getLDIFWriter();
+ ldifWriter2.writeChangeRecord(changeRecord);
+ LDIFReader reader = writer.getLDIFReader();
+ Entry modDNEntry = reader.readEntry();
+ entry = modDNEntry;
}
}
- Attribute changetype = Attributes.create("changetype", "add");
- addAttribute(attributes, changetype);
-
if (exportConfig != null)
{
- AddChangeRecordEntry changeRecord =
- new AddChangeRecordEntry(dn, attributes);
- ldifWriter.writeChangeRecord(changeRecord);
+ this.exportedCount++;
}
else
{
- entry = new Entry(dn, objectclasses, attributes, null);
- }
- }
- else if (msg instanceof DeleteMsg)
- {
- DeleteMsg delMsg = (DeleteMsg)msg;
+ // Add extensibleObject objectclass and the ChangeNumber
+ // in the entry.
+ if (!entry.getObjectClasses().containsKey(objectclass))
+ entry.addObjectClass(objectclass);
+ Attribute changeNumber =
+ Attributes.create(CHANGE_NUMBER,
+ msg.getChangeNumber().toStringUI());
+ addAttribute(entry.getUserAttributes(), changeNumber);
+ Attribute domain = Attributes.create("replicationDomain", baseDN);
+ addAttribute(entry.getUserAttributes(), domain);
- dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
- CHANGE_NUMBER + "=" + delMsg.getChangeNumber().toString()+ "," +
- msg.getDn() +","+ BASE_DN);
+ // Get the base DN, scope, and filter for the search.
+ DN searchBaseDN = searchOperation.getBaseDN();
+ SearchScope scope = searchOperation.getScope();
+ SearchFilter filter = searchOperation.getFilter();
- DeleteChangeRecordEntry changeRecord =
- new DeleteChangeRecordEntry(dn);
- if (exportConfig != null)
- {
- ldifWriter.writeChangeRecord(changeRecord);
- }
- else
- {
- Writer writer = new Writer();
- LDIFWriter ldifWriter2 = writer.getLDIFWriter();
- ldifWriter2.writeChangeRecord(changeRecord);
- LDIFReader reader = writer.getLDIFReader();
- entry = reader.readEntry();
- }
- }
- else if (msg instanceof ModifyMsg)
- {
- ModifyOperation op = (ModifyOperation)msg.createOperation(conn);
-
- dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
- CHANGE_NUMBER + "=" + msg.getChangeNumber().toString()+ "," +
- msg.getDn() +","+ BASE_DN);
- op.setInternalOperation(true);
-
- ModifyChangeRecordEntry changeRecord =
- new ModifyChangeRecordEntry(dn, op.getRawModifications());
- if (exportConfig != null)
- {
- ldifWriter.writeChangeRecord(changeRecord);
- }
- else
- {
- Writer writer = new Writer();
- LDIFWriter ldifWriter2 = writer.getLDIFWriter();
- ldifWriter2.writeChangeRecord(changeRecord);
- LDIFReader reader = writer.getLDIFReader();
- entry = reader.readEntry();
- }
- }
- else if (msg instanceof ModifyDNMsg)
- {
- ModifyDNOperation op = (ModifyDNOperation)msg.createOperation(conn);
-
- dn = DN.decode("uuid=" + msg.getUniqueId() + "," +
- CHANGE_NUMBER + "=" + msg.getChangeNumber().toString()+ "," +
- msg.getDn() +","+ BASE_DN);
- op.setInternalOperation(true);
-
- ModifyDNChangeRecordEntry changeRecord =
- new ModifyDNChangeRecordEntry(dn, op.getNewRDN(), op.deleteOldRDN(),
- op.getNewSuperior());
-
- if (exportConfig != null)
- {
- ldifWriter.writeChangeRecord(changeRecord);
- }
- else
- {
- Writer writer = new Writer();
- LDIFWriter ldifWriter2 = writer.getLDIFWriter();
- ldifWriter2.writeChangeRecord(changeRecord);
- LDIFReader reader = writer.getLDIFReader();
- Entry modDNEntry = reader.readEntry();
- entry = modDNEntry;
- }
- }
-
- if (exportConfig != null)
- {
- this.exportedCount++;
- }
- else
- {
- // Add extensibleObject objectclass and the ChangeNumber
- // in the entry.
- if (!entry.getObjectClasses().containsKey(objectclass))
- entry.addObjectClass(objectclass);
- Attribute changeNumber =
- Attributes.create(CHANGE_NUMBER, msg.getChangeNumber().toStringUI());
- addAttribute(entry.getUserAttributes(), changeNumber);
- Attribute domain = Attributes.create("replicationDomain", baseDN);
- addAttribute(entry.getUserAttributes(), domain);
-
- // Get the base DN, scope, and filter for the search.
- DN searchBaseDN = searchOperation.getBaseDN();
- SearchScope scope = searchOperation.getScope();
- SearchFilter filter = searchOperation.getFilter();
-
- boolean ms = entry.matchesBaseAndScope(searchBaseDN, scope);
- boolean mf = filter.matchesEntry(entry);
- if ( ms && mf )
- {
- searchOperation.returnEntry(entry, new LinkedList<Control>());
+ boolean ms = entry.matchesBaseAndScope(searchBaseDN, scope);
+ boolean mf = filter.matchesEntry(entry);
+ if ( ms && mf )
+ {
+ searchOperation.returnEntry(entry, new LinkedList<Control>());
+ }
}
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationDB.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationDB.java
index 8a69489..f83631a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationDB.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationDB.java
@@ -34,7 +34,6 @@
import java.util.List;
import java.io.UnsupportedEncodingException;
-import org.opends.server.types.DN;
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.protocol.UpdateMsg;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -59,7 +58,7 @@
private ReplicationDbEnv dbenv = null;
private ReplicationServer replicationServer;
private Short serverId;
- private DN baseDn;
+ private String baseDn;
// The maximum number of retries in case of DatabaseDeadlock Exception.
private static final int DEADLOCK_RETRIES = 10;
@@ -77,7 +76,7 @@
* @param dbenv The Db environment to use to create the db.
* @throws DatabaseException If a database problem happened.
*/
- public ReplicationDB(Short serverId, DN baseDn,
+ public ReplicationDB(Short serverId, String baseDn,
ReplicationServer replicationServer,
ReplicationDbEnv dbenv)
throws DatabaseException
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationDbEnv.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationDbEnv.java
index 01ba275..98aa2ee 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationDbEnv.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationDbEnv.java
@@ -37,9 +37,6 @@
import java.io.File;
import java.io.UnsupportedEncodingException;
-import org.opends.server.types.DN;
-import org.opends.server.types.DirectoryException;
-
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
@@ -148,7 +145,7 @@
{
long generationId=-1;
- DN baseDn;
+ String baseDn;
try
{
@@ -165,18 +162,7 @@
+ "<" + str[1] + ">"));
}
- // <baseDn>
- baseDn = null;
- try
- {
- baseDn = DN.decode(str[2]);
- } catch (DirectoryException e)
- {
- Message message =
- ERR_IGNORE_BAD_DN_IN_DATABASE_IDENTIFIER.get(str[1]);
- logError(message);
-
- }
+ baseDn = str[2];
if (debugEnabled())
TRACER.debugInfo(
@@ -239,16 +225,7 @@
+ "<" + str[0] + ">"));
}
// <baseDn>
- DN baseDn = null;
- try
- {
- baseDn = DN.decode(str[1]);
- } catch (DirectoryException e)
- {
- Message message =
- ERR_IGNORE_BAD_DN_IN_DATABASE_IDENTIFIER.get(str[1]);
- logError(message);
- }
+ String baseDn = str[1];
if (debugEnabled())
TRACER.debugInfo(
@@ -287,7 +264,7 @@
* @return the Database.
* @throws DatabaseException in case of underlying Exception.
*/
- public Database getOrAddDb(Short serverId, DN baseDn, Long generationId)
+ public Database getOrAddDb(Short serverId, String baseDn, Long generationId)
throws DatabaseException
{
if (debugEnabled())
@@ -295,8 +272,7 @@
serverId + " " + baseDn + " " + generationId);
try
{
- String stringId = serverId.toString() + FIELD_SEPARATOR
- + baseDn.toNormalizedString();
+ String stringId = serverId.toString() + FIELD_SEPARATOR + baseDn;
// Opens the database for the changes received from this server
// on this domain. Create it if it does not already exist.
@@ -333,11 +309,9 @@
// Creates the record domain base Dn/ generationId in the stateDb
// if it does not already exist.
- stringId = GENERATION_ID_TAG + FIELD_SEPARATOR +
- baseDn.toNormalizedString();
+ stringId = GENERATION_ID_TAG + FIELD_SEPARATOR + baseDn;
String dataStringId = GENERATION_ID_TAG + FIELD_SEPARATOR +
- generationId.toString() + FIELD_SEPARATOR +
- baseDn.toNormalizedString();
+ generationId.toString() + FIELD_SEPARATOR + baseDn;
byteId = stringId.getBytes("UTF-8");
byte[] dataByteId;
dataByteId = dataStringId.getBytes("UTF-8");
@@ -408,7 +382,7 @@
* @param baseDn The baseDn for which the generationID must be cleared.
*
*/
- public void clearGenerationId(DN baseDn)
+ public void clearGenerationId(String baseDn)
{
if (debugEnabled())
TRACER.debugInfo(
@@ -417,8 +391,7 @@
try
{
// Deletes the record domain base Dn/ generationId in the stateDb
- String stringId = GENERATION_ID_TAG + FIELD_SEPARATOR +
- baseDn.toNormalizedString();
+ String stringId = GENERATION_ID_TAG + FIELD_SEPARATOR + baseDn;
byte[] byteId = stringId.getBytes("UTF-8");
DatabaseEntry key = new DatabaseEntry();
key.setData(byteId);
@@ -435,8 +408,7 @@
if (debugEnabled())
TRACER.debugInfo(
"In " + this.replicationServer.getMonitorInstanceName() +
- " clearGenerationId (" +
- baseDn +") succeeded.");
+ " clearGenerationId (" + baseDn +") succeeded.");
}
catch (DatabaseException dbe)
{
@@ -472,7 +444,7 @@
* @param serverId The serverId to remove from the Db.
*
*/
- public void clearServerId(DN baseDn, Short serverId)
+ public void clearServerId(String baseDn, Short serverId)
{
if (debugEnabled())
TRACER.debugInfo(
@@ -480,8 +452,7 @@
"clearServerId(baseDN=" + baseDn + ", serverId=" + serverId);
try
{
- String stringId = serverId.toString() + FIELD_SEPARATOR
- + baseDn.toNormalizedString();
+ String stringId = serverId.toString() + FIELD_SEPARATOR + baseDn;
// Deletes the record serverId/domain base Dn in the stateDb
byte[] byteId;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServer.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
index 0175414..be59878 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
@@ -108,8 +108,8 @@
/* This table is used to store the list of dn for which we are currently
* handling servers.
*/
- private ConcurrentHashMap<DN, ReplicationServerDomain> baseDNs =
- new ConcurrentHashMap<DN, ReplicationServerDomain>();
+ private ConcurrentHashMap<String, ReplicationServerDomain> baseDNs =
+ new ConcurrentHashMap<String, ReplicationServerDomain>();
private String localURL = "null";
private boolean shutdown = false;
@@ -202,7 +202,7 @@
assuredTimeout = configuration.getAssuredTimeout();
degradedStatusThreshold = configuration.getDegradedStatusThreshold();
- replSessionSecurity = new ReplSessionSecurity(configuration);
+ replSessionSecurity = new ReplSessionSecurity();
initialize(replicationPort);
configuration.addChangeListener(this);
DirectoryServer.registerMonitorProvider(this);
@@ -360,7 +360,7 @@
* colon.
* @param baseDn The baseDn of the connection
*/
- private void connect(String serverURL, DN baseDn)
+ private void connect(String serverURL, String baseDn)
{
int separator = serverURL.lastIndexOf(':');
String port = serverURL.substring(separator + 1);
@@ -484,7 +484,7 @@
* @return The ReplicationServerDomain associated to the base DN given in
* parameter.
*/
- public ReplicationServerDomain getReplicationServerDomain(DN baseDn,
+ public ReplicationServerDomain getReplicationServerDomain(String baseDn,
boolean create)
{
ReplicationServerDomain replicationServerDomain;
@@ -560,7 +560,7 @@
* DN given in parameter.
* @throws DatabaseException in case of underlying database problem.
*/
- public DbHandler newDbHandler(short id, DN baseDn)
+ public DbHandler newDbHandler(short id, String baseDn)
throws DatabaseException
{
return new DbHandler(id, baseDn, this, dbEnv, queueSize);
@@ -572,7 +572,7 @@
* @param baseDn The baseDn for which to delete the generationId.
* @throws DatabaseException When it occurs.
*/
- public void clearGenerationId(DN baseDn)
+ public void clearGenerationId(String baseDn)
throws DatabaseException
{
try
@@ -799,7 +799,7 @@
* Add all the base DNs that are known by this replication server.
*/
AttributeBuilder builder = new AttributeBuilder("base-dn");
- for (DN base : baseDNs.keySet())
+ for (String base : baseDNs.keySet())
{
builder.add(base.toString());
}
@@ -807,7 +807,7 @@
// Publish to monitor the generation ID by replicationServerDomain
builder = new AttributeBuilder("base-dn-generation-id");
- for (DN base : baseDNs.keySet())
+ for (String base : baseDNs.keySet())
{
long generationId=-1;
ReplicationServerDomain replicationServerDomain =
@@ -828,7 +828,7 @@
* @param baseDN The baseDN of the replicationServerDomain.
* @return The value of the generationID.
*/
- public long getGenerationId(DN baseDN)
+ public long getGenerationId(String baseDN)
{
ReplicationServerDomain rsd =
this.getReplicationServerDomain(baseDN, false);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
index a7c3dae..cf68944 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
@@ -50,7 +50,6 @@
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.common.ServerState;
-import org.opends.server.replication.protocol.AckMsg;
import org.opends.server.replication.protocol.ErrorMsg;
import org.opends.server.replication.protocol.RoutableMsg;
import org.opends.server.replication.protocol.UpdateMsg;
@@ -58,17 +57,21 @@
import org.opends.server.replication.protocol.MonitorMsg;
import org.opends.server.replication.protocol.MonitorRequestMsg;
import org.opends.server.replication.protocol.ResetGenerationIdMsg;
-import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.ResultCode;
import org.opends.server.util.TimeThread;
import com.sleepycat.je.DatabaseException;
+import java.util.Timer;
+import java.util.TimerTask;
import java.util.concurrent.locks.ReentrantLock;
+import org.opends.server.replication.common.AssuredMode;
import org.opends.server.replication.common.DSInfo;
import org.opends.server.replication.common.RSInfo;
import org.opends.server.replication.common.ServerStatus;
import org.opends.server.replication.common.StatusMachineEvent;
+import org.opends.server.replication.protocol.AckMsg;
import org.opends.server.replication.protocol.ChangeStatusMsg;
+import org.opends.server.replication.protocol.ProtocolVersion;
/**
* This class define an in-memory cache that will be used to store
@@ -88,9 +91,8 @@
*/
public class ReplicationServerDomain
{
-
private final Object flowControlLock = new Object();
- private final DN baseDn;
+ private final String baseDn;
// The Status analyzer that periodically verifis if the connected DSs are
// late or not
private StatusAnalyzer statusAnalyzer = null;
@@ -154,16 +156,38 @@
private MonitorData wrkMonitorData;
/**
+ * The needed info for each received assured update message we are waiting
+ * acks for.
+ * Key: a change number matching a received update message which requested
+ * assured mode usage (either safe read or safe data mode)
+ * Value: The object holding every info needed about the already received acks
+ * as well as the acks to be received.
+ * For more details, see ExpectedAcksInfo and its sub classes javadoc.
+ */
+ private final ConcurrentHashMap<ChangeNumber, ExpectedAcksInfo> waitingAcks =
+ new ConcurrentHashMap<ChangeNumber, ExpectedAcksInfo>();
+
+ // The timer used to run the timeout code (timer tasks) for the assured update
+ // messages we are waiting acks for.
+ private Timer assuredTimeoutTimer = null;
+ // Counter used to purge the timer tasks referemces in assuredTimeoutTimer,
+ // every n number of treated assured messages
+ private int assuredTimeoutTimerPurgeCounter = 0;
+
+ /**
* Creates a new ReplicationServerDomain associated to the DN baseDn.
*
* @param baseDn The baseDn associated to the ReplicationServerDomain.
* @param replicationServer the ReplicationServer that created this
* replicationServer cache.
*/
- public ReplicationServerDomain(DN baseDn, ReplicationServer replicationServer)
+ public ReplicationServerDomain(
+ String baseDn, ReplicationServer replicationServer)
{
this.baseDn = baseDn;
this.replicationServer = replicationServer;
+ this.assuredTimeoutTimer = new Timer("Replication Assured Timer for " +
+ baseDn + " in RS " + replicationServer.getServerId(), true);
}
/**
@@ -179,32 +203,11 @@
public void put(UpdateMsg update, ServerHandler sourceHandler)
throws IOException
{
- /*
- * TODO : In case that the source server is a LDAP server this method
- * should check that change did get pushed to at least one
- * other replication server before pushing it to the LDAP servers
- */
-
- short id = update.getChangeNumber().getServerId();
+ ChangeNumber cn = update.getChangeNumber();
+ short id = cn.getServerId();
sourceHandler.updateServerState(update);
sourceHandler.incrementInCount();
- if (update.isAssured())
- {
- int count = this.NumServers();
- if (count > 1)
- {
- if (sourceHandler.isReplicationServer())
- ServerHandler.addWaitingAck(update, sourceHandler.getServerId(),
- this, count - 1);
- else
- sourceHandler.addWaitingAck(update, count - 1);
- } else
- {
- sourceHandler.sendAck(update.getChangeNumber());
- }
- }
-
if (generationId < 0)
{
generationId = sourceHandler.getGenerationId();
@@ -244,11 +247,103 @@
// Publish the messages to the source handler
dbHandler.add(update);
+ /**
+ * If this is an assured message (a message requesting ack), we must
+ * construct the ExpectedAcksInfo object with the right number of expected
+ * acks before posting message to the writers. Otherwise some writers may
+ * have time to post, receive the ack and increment received ack counter
+ * (kept in ExpectedAcksInfo object) and we could think the acknowledgment
+ * is fully processed although it may be not (some other acks from other
+ * servers are not yet arrived). So for that purpose we do a pre-loop
+ * to determine to who we will post an assured message.
+ * Whether the assured mode is safe read or safe data, we anyway do not
+ * support the assured replication feature across topologies with different
+ * group ids. The assured feature insures assured replication based on the
+ * same locality (group id). For instance in double data center deployment
+ * (2 group id usage) with assured replication enabled, an assured message
+ * sent from data center 1 (group id = 1) will be sent to servers of both
+ * data centers, but one will request and wait acks only from servers of the
+ * data center 1.
+ */
+ boolean assuredMessage = update.isAssured();
+ PreparedAssuredInfo preparedAssuredInfo = null;
+ if (assuredMessage)
+ {
+ // Assured feature is supported starting from replication protocol V2
+ if (sourceHandler.getProtocolVersion() >=
+ ProtocolVersion.REPLICATION_PROTOCOL_V2)
+ {
+ // According to assured sub-mode, prepare structures to keep track of
+ // the acks we are interested in.
+ AssuredMode assuredMode = update.getAssuredMode();
+ if (assuredMode != AssuredMode.SAFE_DATA_MODE)
+ {
+ preparedAssuredInfo = processSafeDataUpdateMsg(update, sourceHandler);
+ } else if (assuredMode != AssuredMode.SAFE_READ_MODE)
+ {
+ preparedAssuredInfo = processSafeReadUpdateMsg(update, sourceHandler);
+ } else
+ {
+ // Unknown assured mode: should never happen
+ Message errorMsg = ERR_RS_UNKNOWN_ASSURED_MODE.get(
+ Short.toString(replicationServer.getServerId()),
+ assuredMode.toString(), baseDn, update.toString());
+ logError(errorMsg);
+ assuredMessage = false;
+ }
+ } else
+ {
+ assuredMessage = false;
+ }
+ }
+
+ List<Short> expectedServers = null;
+ if (assuredMessage)
+ {
+ expectedServers = preparedAssuredInfo.expectedServers;
+ if (expectedServers != null)
+ {
+ // Store the expected acks info into the global map.
+ // The code for processing reception of acks for this update will update
+ // info kept in this object and if enough acks received, it will send
+ // back the final ack to the requester and remove the object from this
+ // map
+ // OR
+ // The following timer will time out and send an timeout ack to the
+ // requester if the acks are not received in time. The timer will also
+ // remove the object from this map.
+ waitingAcks.put(cn, preparedAssuredInfo.expectedAcksInfo);
+
+ // Arm timer for this assured update message (wait for acks until it
+ // times out)
+ AssuredTimeoutTask assuredTimeoutTask = new AssuredTimeoutTask(cn);
+ assuredTimeoutTimer.schedule(assuredTimeoutTask,
+ replicationServer.getAssuredTimeout());
+ // Purge timer every 100 treated messages
+ assuredTimeoutTimerPurgeCounter++;
+ if ((assuredTimeoutTimerPurgeCounter % 100) == 0)
+ assuredTimeoutTimer.purge();
+ }
+ }
+
+ /**
+ * The update message equivalent to the originally received update message,
+ * but with assured flag disabled. This message is the one that should be
+ * sent to non elligible servers for assured mode.
+ * We need a clone like of the original message with assured flag off, to be
+ * posted to servers we don't want to wait the ack from (not normal status
+ * servers or servers with different group id). This must be done because
+ * the posted message is a reference so each writer queue gets the same
+ * reference, thus, changing the assured flag of an object is done for every
+ * references posted on every writer queues. That is why we need a message
+ * version with assured flag on and another one with assured flag off.
+ */
+ NotAssuredUpdateMsg notAssuredUpdate = null;
/*
* Push the message to the replication servers
*/
- if (!sourceHandler.isReplicationServer())
+ if (sourceHandler.isLDAPserver())
{
for (ServerHandler handler : replicationServers.values())
{
@@ -261,7 +356,7 @@
if (debugEnabled())
TRACER.debugInfo("In RS " +
replicationServer.getServerId() +
- " for dn " + baseDn.toNormalizedString() + ", update " +
+ " for dn " + baseDn + ", update " +
update.getChangeNumber().toString() +
" will not be sent to replication server " +
Short.toString(handler.getServerId()) + " with generation id " +
@@ -272,7 +367,27 @@
continue;
}
- handler.add(update, sourceHandler);
+ if (assuredMessage)
+ {
+ // Assured mode: post an assured or not assured matching update
+ // message according to what has been computed for the destination
+ // server
+ if ((expectedServers != null) && expectedServers.contains(handler.
+ getServerId()))
+ {
+ handler.add(update, sourceHandler);
+ } else
+ {
+ if (notAssuredUpdate == null)
+ {
+ notAssuredUpdate = new NotAssuredUpdateMsg(update);
+ }
+ handler.add(notAssuredUpdate, sourceHandler);
+ }
+ } else
+ {
+ handler.add(update, sourceHandler);
+ }
}
}
@@ -281,7 +396,7 @@
*/
for (ServerHandler handler : directoryServers.values())
{
- // don't forward the change to the server that just sent it
+ // Don't forward the change to the server that just sent it
if (handler == sourceHandler)
{
continue;
@@ -307,7 +422,7 @@
if (dsStatus == ServerStatus.BAD_GEN_ID_STATUS)
TRACER.debugInfo("In RS " +
replicationServer.getServerId() +
- " for dn " + baseDn.toNormalizedString() + ", update " +
+ " for dn " + baseDn + ", update " +
update.getChangeNumber().toString() +
" will not be sent to directory server " +
Short.toString(handler.getServerId()) + " with generation id " +
@@ -317,7 +432,7 @@
if (dsStatus == ServerStatus.FULL_UPDATE_STATUS)
TRACER.debugInfo("In RS " +
replicationServer.getServerId() +
- " for dn " + baseDn.toNormalizedString() + ", update " +
+ " for dn " + baseDn + ", update " +
update.getChangeNumber().toString() +
" will not be sent to directory server " +
Short.toString(handler.getServerId()) +
@@ -327,9 +442,389 @@
continue;
}
- handler.add(update, sourceHandler);
+ if (assuredMessage)
+ {
+ // Assured mode: post an assured or not assured matching update
+ // message according to what has been computed for the destination
+ // server
+ if ((expectedServers != null) && expectedServers.contains(handler.
+ getServerId()))
+ {
+ handler.add(update, sourceHandler);
+ } else
+ {
+ if (notAssuredUpdate == null)
+ {
+ notAssuredUpdate = new NotAssuredUpdateMsg(update);
+ }
+ handler.add(notAssuredUpdate, sourceHandler);
+ }
+ } else
+ {
+ handler.add(update, sourceHandler);
+ }
+ }
+ }
+
+ /**
+ * Helper class to be the return type of a method that processes a just
+ * received assured update message:
+ * - processSafeReadUpdateMsg
+ * - processSafeDataUpdateMsg
+ * This is a facility to pack many interesting returned object.
+ */
+ private class PreparedAssuredInfo
+ {
+ /**
+ * The list of servers identified as servers we are interested in
+ * receiving acks from. If this list is not null, then expectedAcksInfo
+ * should be not null.
+ * Servers that are not in this list are servers not elligible for an ack
+ * request.
+ *
+ */
+ public List<Short> expectedServers = null;
+
+ /**
+ * The constructed ExpectedAcksInfo object to be used when acks will be
+ * received. Null if expectedServers is null.
+ */
+ public ExpectedAcksInfo expectedAcksInfo = null;
+ }
+
+ /**
+ * Process a just received assured update message in Safe Read mode. If the
+ * ack can be sent immediately, it is done here. This will also determine to
+ * which suitable servers an ack should be requested from, and which ones are
+ * not elligible for an ack request.
+ * This method is an helper method for the put method. Have a look at the put
+ * method for a better understanding.
+ * @param update The just received assured update to process.
+ * @param sourceHandler The ServerHandler for the server from which the
+ * update was received
+ * @return A suitable PreparedAssuredInfo object that contains every needed
+ * info to proceed with post to server writers.
+ * @throws IOException When an IO exception happens during the update
+ * processing.
+ */
+ private PreparedAssuredInfo processSafeReadUpdateMsg(
+ UpdateMsg update, ServerHandler sourceHandler) throws IOException
+ {
+ ChangeNumber cn = update.getChangeNumber();
+ byte groupId = replicationServer.getGroupId();
+ byte sourceGroupId = sourceHandler.getGroupId();
+ List<Short> expectedServers = new ArrayList<Short>();
+ List<Short> wrongStatusServers = new ArrayList<Short>();
+
+ if (sourceGroupId != groupId)
+ // Assured feature does not cross different group ids
+ {
+ if (sourceHandler.isLDAPserver())
+ {
+ // Look for RS elligible for assured
+ for (ServerHandler handler : replicationServers.values())
+ {
+ if (handler.getGroupId() == groupId)
+ expectedServers.add(handler.getServerId());
+ }
+ }
+
+ // Look for DS elligible for assured
+ for (ServerHandler handler : directoryServers.values())
+ {
+ // Don't forward the change to the server that just sent it
+ if (handler == sourceHandler)
+ {
+ continue;
+ }
+ if (handler.getGroupId() == groupId)
+ {
+ if (handler.getStatus() == ServerStatus.NORMAL_STATUS)
+ {
+ expectedServers.add(handler.getServerId());
+ } else
+ {
+ wrongStatusServers.add(handler.getServerId());
+ }
+ }
+ }
}
+ // Return computed structures
+ PreparedAssuredInfo preparedAssuredInfo = new PreparedAssuredInfo();
+ if (expectedServers.size() > 0)
+ {
+ // Some other acks to wait for
+ preparedAssuredInfo.expectedAcksInfo = new SafeReadExpectedAcksInfo(cn,
+ sourceHandler, expectedServers, wrongStatusServers);
+ preparedAssuredInfo.expectedServers = expectedServers;
+ }
+
+ if (preparedAssuredInfo.expectedServers == null)
+ {
+ // No elligible servers found, send the ack immediatly
+ AckMsg ack = new AckMsg(cn);
+ sourceHandler.sendAck(ack);
+ }
+
+ return preparedAssuredInfo;
+ }
+
+ /**
+ * Process a just received assured update message in Safe Data mode. If the
+ * ack can be sent immediately, it is done here. This will also determine to
+ * which suitable servers an ack should be requested from, and which ones are
+ * not elligible for an ack request.
+ * This method is an helper method for the put method. Have a look at the put
+ * method for a better understanding.
+ * @param update The just received assured update to process.
+ * @param sourceHandler The ServerHandler for the server from which the
+ * update was received
+ * @return A suitable PreparedAssuredInfo object that contains every needed
+ * info to proceed with post to server writers.
+ * @throws IOException When an IO exception happens during the update
+ * processing.
+ */
+ private PreparedAssuredInfo processSafeDataUpdateMsg(
+ UpdateMsg update, ServerHandler sourceHandler) throws IOException
+ {
+ ChangeNumber cn = update.getChangeNumber();
+ boolean interestedInAcks = true;
+ byte safeDataLevel = update.getSafeDataLevel();
+ byte groupId = replicationServer.getGroupId();
+ byte sourceGroupId = sourceHandler.getGroupId();
+ if (safeDataLevel < (byte) 1)
+ {
+ // Should never happen
+ Message errorMsg = ERR_UNKNOWN_ASSURED_SAFE_DATA_LEVEL.get(
+ Short.toString(replicationServer.getServerId()),
+ Byte.toString(safeDataLevel), baseDn, update.toString());
+ logError(errorMsg);
+ interestedInAcks = false;
+ } else if (sourceGroupId != groupId)
+ {
+ // Assured feature does not cross different group ids
+ interestedInAcks = false;
+ } else
+ {
+ if (sourceHandler.isLDAPserver())
+ {
+ if (safeDataLevel == (byte) 1)
+ {
+ // Immediatly return the ack for an assured message in safe data mode
+ // with safe data level 1, coming from a DS. No need to wait for more
+ // acks
+ AckMsg ack = new AckMsg(cn);
+ sourceHandler.sendAck(ack);
+ interestedInAcks = false; // No further acks to obtain
+ } else
+ {
+ // level > 1 : We need further acks
+ // The message will be posted in assured mode to elligible servers.
+ // The embedded safe data level is not changed, and his value will be
+ // used by a remote RS to determine if he must send an ack (level > 1)
+ // or not (level = 1)
+ }
+ } else
+ { // A RS sent us the safe data message, for sure no futher acks to wait
+ interestedInAcks = false;
+ if (safeDataLevel == (byte) 1)
+ {
+ // The original level was 1 so the RS that sent us this message should
+ // have already sent his ack to the sender DS. Level 1 has already
+ // been reached so no further acks to wait
+ // This should not happen in theory as the sender RS server should
+ // have sent us a matching not assured message so we should not come
+ // to here.
+ } else
+ {
+ // level > 1, so Ack this message to originator RS
+ AckMsg ack = new AckMsg(cn);
+ sourceHandler.sendAck(ack);
+ }
+ }
+ }
+
+ List<Short> expectedServers = new ArrayList<Short>();
+ if (interestedInAcks)
+ {
+ if (sourceHandler.isLDAPserver())
+ {
+ // Look for RS elligible for assured
+ for (ServerHandler handler : replicationServers.values())
+ {
+ if (handler.getGroupId() == groupId)
+ expectedServers.add(handler.getServerId());
+ }
+ }
+
+ // Look for DS elligible for assured
+ for (ServerHandler handler : directoryServers.values())
+ {
+ // Don't forward the change to the server that just sent it
+ if (handler == sourceHandler)
+ {
+ continue;
+ }
+ if (handler.getGroupId() == groupId)
+ expectedServers.add(handler.getServerId());
+ }
+ }
+
+ // Return computed structures
+ PreparedAssuredInfo preparedAssuredInfo = new PreparedAssuredInfo();
+ if (interestedInAcks && (expectedServers.size() > 0))
+ {
+ // Some other acks to wait for
+ preparedAssuredInfo.expectedAcksInfo = new SafeDataExpectedAcksInfo(cn,
+ sourceHandler, update.getSafeDataLevel());
+ preparedAssuredInfo.expectedServers = expectedServers;
+ }
+
+ if (interestedInAcks && (preparedAssuredInfo.expectedServers == null))
+ {
+ // level > 1 and source is a DS but no elligible servers found, send the
+ // ack immediatly
+ AckMsg ack = new AckMsg(cn);
+ sourceHandler.sendAck(ack);
+ }
+
+ return preparedAssuredInfo;
+ }
+
+ /**
+ * Process an ack received from a given server.
+ *
+ * @param ack The ack message received.
+ * @param ackingServer The server handler of the server that sent the ack.
+ */
+ public void processAck(AckMsg ack, ServerHandler ackingServer)
+ {
+ // Retrieve the expected acks info for the update matching the original
+ // sent update.
+ ChangeNumber cn = ack.getChangeNumber();
+ ExpectedAcksInfo expectedAcksInfo = waitingAcks.get(cn);
+
+ if (expectedAcksInfo != null)
+ {
+ // Prevent concurrent access from processAck() or AssuredTimeoutTask.run()
+ synchronized (expectedAcksInfo)
+ {
+ if (expectedAcksInfo.isCompleted())
+ {
+ // Timeout code is sending a timeout ack, do nothing and let him
+ // remove object from the map
+ return;
+ }
+ // If this is the last ack we were waiting from, immediatly create and
+ // send the final ack to the original server
+ if (expectedAcksInfo.processReceivedAck(ackingServer, ack))
+ {
+ // Remove the object from the map as no more needed
+ waitingAcks.remove(cn);
+ AckMsg finalAck = expectedAcksInfo.createAck(false);
+ ServerHandler origServer = expectedAcksInfo.getRequesterServer();
+ try
+ {
+ origServer.sendAck(finalAck);
+ } catch (IOException e)
+ {
+ /*
+ * An error happened trying the send back an ack to the server.
+ * Log an error and close the connection to this server.
+ */
+ MessageBuilder mb = new MessageBuilder();
+ mb.append(ERR_RS_ERROR_SENDING_ACK.get(
+ Short.toString(replicationServer.getServerId()),
+ Short.toString(origServer.getServerId()), cn.toString(), baseDn));
+ mb.append(stackTraceToSingleLineString(e));
+ logError(mb.toMessage());
+ stopServer(origServer);
+ }
+ // Mark the ack info object as completed to prevent potential timeout
+ // code parallel run
+ expectedAcksInfo.completed();
+ }
+ }
+ } else
+ {
+ // The timeout occured for the update matching this change number and the
+ // ack with timeout error has probably already been sent.
+ }
+ }
+
+ /**
+ * The code run when the timeout occurs while waiting for acks of the
+ * elligible servers. This basically sends a timeout ack (with any additional
+ * error info) to the original server that sent an assured update message.
+ */
+ private class AssuredTimeoutTask extends TimerTask
+ {
+ private ChangeNumber cn = null;
+
+ /**
+ * Constructor for the timer task.
+ * @param cn The changenumber of the assured update we are waiting acks for
+ */
+ public AssuredTimeoutTask(ChangeNumber cn)
+ {
+ this.cn = cn;
+ }
+
+ /**
+ * Run when the assured timeout for an assured update message we are waiting
+ * acks for occurs.
+ */
+ public void run()
+ {
+ ExpectedAcksInfo expectedAcksInfo = waitingAcks.get(cn);
+
+ if (expectedAcksInfo != null)
+ {
+ synchronized (expectedAcksInfo)
+ {
+ if (expectedAcksInfo.isCompleted())
+ {
+ // processAck() code is sending the ack, do nothing and let him
+ // remove object from the map
+ return;
+ }
+ // Remove the object from the map as no more needed
+ waitingAcks.remove(cn);
+ // Create the timeout ack and send him to the server the assured
+ // update message came from
+ AckMsg finalAck = expectedAcksInfo.createAck(true);
+ ServerHandler origServer = expectedAcksInfo.getRequesterServer();
+ if (debugEnabled())
+ TRACER.debugInfo(
+ "In RS " + Short.toString(replicationServer.getServerId()) +
+ " for " + baseDn +
+ ", sending timeout for assured update with change " + " number " +
+ cn.toString() + " to server id " +
+ Short.toString(origServer.getServerId()));
+ try
+ {
+ origServer.sendAck(finalAck);
+ } catch (IOException e)
+ {
+ /*
+ * An error happened trying the send back an ack to the server.
+ * Log an error and close the connection to this server.
+ */
+ MessageBuilder mb = new MessageBuilder();
+ mb.append(ERR_RS_ERROR_SENDING_ACK.get(
+ Short.toString(replicationServer.getServerId()),
+ Short.toString(origServer.getServerId()), cn.toString(), baseDn));
+ mb.append(stackTraceToSingleLineString(e));
+ logError(mb.toMessage());
+ stopServer(origServer);
+ }
+ // Mark the ack info object as completed to prevent potential
+ // processAck() code parallel run
+ expectedAcksInfo.completed();
+ }
+ }
+ }
}
/**
@@ -687,7 +1182,7 @@
* Get the baseDn.
* @return Returns the baseDn.
*/
- public DN getBaseDn()
+ public String getBaseDn()
{
return baseDn;
}
@@ -711,44 +1206,6 @@
}
/**
- * Get the number of currently connected servers.
- *
- * @return the number of currently connected servers.
- */
- private int NumServers()
- {
- return replicationServers.size() + directoryServers.size();
- }
-
- /**
- * Add an ack to the list of ack received for a given change.
- *
- * @param message The ack message received.
- * @param fromServerId The identifier of the server that sent the ack.
- */
- public void ack(AckMsg message, short fromServerId)
- {
- /*
- * there are 2 possible cases here :
- * - the message that was acked comes from a server to which
- * we are directly connected.
- * In this case, we can find the handler from the directoryServers map
- * - the message that was acked comes from a server to which we are not
- * connected.
- * In this case we need to find the replication server that forwarded
- * the change and send back the ack to this server.
- */
- ServerHandler handler = directoryServers.get(
- message.getChangeNumber().getServerId());
- if (handler != null)
- handler.ack(message, fromServerId);
- else
- {
- ServerHandler.ackChangelog(message, fromServerId);
- }
- }
-
- /**
* Retrieves the destination handlers for a routable message.
*
* @param msg The message to route.
@@ -975,56 +1432,6 @@
}
/**
- * Send back an ack to the server that sent the change.
- *
- * @param changeNumber The ChangeNumber of the change that must be acked.
- * @param isLDAPserver This boolean indicates if the server that sent the
- * change was an LDAP server or a ReplicationServer.
- */
- public void sendAck(ChangeNumber changeNumber, boolean isLDAPserver)
- {
- short serverId = changeNumber.getServerId();
- sendAck(changeNumber, isLDAPserver, serverId);
- }
-
- /**
- *
- * Send back an ack to a server that sent the change.
- *
- * @param changeNumber The ChangeNumber of the change that must be acked.
- * @param isLDAPserver This boolean indicates if the server that sent the
- * change was an LDAP server or a ReplicationServer.
- * @param serverId The identifier of the server from which we
- * received the change..
- */
- public void sendAck(ChangeNumber changeNumber, boolean isLDAPserver,
- short serverId)
- {
- ServerHandler handler;
- if (isLDAPserver)
- handler = directoryServers.get(serverId);
- else
- handler = replicationServers.get(serverId);
-
- // TODO : check for null handler and log error
- try
- {
- handler.sendAck(changeNumber);
- } catch (IOException e)
- {
- /*
- * An error happened trying the send back an ack to this server.
- * Log an error and close the connection to this server.
- */
- MessageBuilder mb = new MessageBuilder();
- mb.append(ERR_CHANGELOG_ERROR_SENDING_ACK.get(this.toString()));
- mb.append(stackTraceToSingleLineString(e));
- logError(mb.toMessage());
- stopServer(handler);
- }
- }
-
- /**
* Shutdown this ReplicationServerDomain.
*/
public void shutdown()
@@ -1228,13 +1635,8 @@
{
// Put RS info
rsInfos.add(serverHandler.toRSInfo());
- // Put his DSs info
- Map<Short, LightweightServerHandler> lsList =
- serverHandler.getConnectedDSs();
- for (LightweightServerHandler ls : lsList.values())
- {
- dsInfos.add(ls.toDSInfo());
- }
+
+ serverHandler.addDSInfos(dsInfos);
}
return new TopologyMsg(dsInfos, rsInfos);
@@ -1641,7 +2043,7 @@
if (generationId > 0 && (generationId != handler.getGenerationId()))
{
Message message = NOTE_BAD_GENERATION_ID_FROM_RS.get(
- baseDn.toNormalizedString(),
+ baseDn,
Short.toString(handler.getServerId()),
Long.toString(handler.getGenerationId()),
Long.toString(generationId));
@@ -1879,10 +2281,13 @@
synchronized (wrkMonitorData)
{
// Here is the RS state : list <serverID, lastChangeNumber>
- // For each LDAP Server, we keep the max CN accross the RSes
+ // For each LDAP Server, we keep the max CN across the RSes
ServerState replServerState = msg.getReplServerDbState();
wrkMonitorData.setMaxCNs(replServerState);
+ // store the remote RS states.
+ wrkMonitorData.setRSState(msg.getsenderID(), replServerState);
+
// Store the remote LDAP servers states
Iterator<Short> lsidIterator = msg.ldapIterator();
while (lsidIterator.hasNext())
@@ -2092,3 +2497,4 @@
}
}
}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/SafeDataExpectedAcksInfo.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/SafeDataExpectedAcksInfo.java
new file mode 100644
index 0000000..e4ce2c4
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/SafeDataExpectedAcksInfo.java
@@ -0,0 +1,110 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.replication.server;
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.protocol.AckMsg;
+
+/**
+ * This class holds every info needed about the expected acks for a received
+ * update message requesting assured replication with Safe Data sub-mode.
+ * It also includes info/routines for constructing the final ack to be sent to
+ * the sender of the update message.
+ */
+public class SafeDataExpectedAcksInfo extends ExpectedAcksInfo
+{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+ // Requested level of safe data when the update message was received.
+ private byte safeDataLevel = (byte)-1;
+
+ // Number of received acks for the matching update message, up to now
+ // Already set to 1 as the local RS receiving the message from a DS counts.
+ private byte numReceivedAcks = (byte)1;
+
+ /**
+ * Creates a new SafeDataExpectedAcksInfo.
+ * @param changeNumber The change number of the assured update message
+ * @param requesterServerHandler The server that sent the assured update
+ * message
+ * @param safeDataLevel The Safe Data level requested for the assured
+ * update message
+ */
+ public SafeDataExpectedAcksInfo(ChangeNumber changeNumber,
+ ServerHandler requesterServerHandler, byte safeDataLevel)
+ {
+ super(changeNumber, requesterServerHandler, AssuredMode.SAFE_DATA_MODE);
+ this.safeDataLevel = safeDataLevel;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean processReceivedAck(ServerHandler ackingServer, AckMsg ackMsg)
+ {
+ /*
+ * Security: although a DS should not respond to an update message sent to
+ * him with assured safe data mode, we double check here that the ack sender
+ * is a RS to take the ack into account.
+ */
+ if (ackingServer.isLDAPserver())
+ {
+ // Sanity check: this should never happen
+ if (debugEnabled())
+ TRACER.debugInfo("Received unexpected SD ack from DS id: "
+ + ackingServer.getServerId() + " ack message: " + ackMsg);
+ return false;
+ }
+
+ numReceivedAcks++;
+ if (numReceivedAcks == safeDataLevel)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public AckMsg createAck(boolean timeout)
+ {
+ AckMsg ack = new AckMsg(changeNumber);
+
+ if (timeout)
+ ack.setHasTimeout(true);
+
+ return ack;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/SafeReadExpectedAcksInfo.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/SafeReadExpectedAcksInfo.java
new file mode 100644
index 0000000..62fb049
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/SafeReadExpectedAcksInfo.java
@@ -0,0 +1,240 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.replication.server;
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
+import org.opends.server.loggers.debug.DebugTracer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.protocol.AckMsg;
+
+/**
+ * This class holds every info needed about the expected acks for a received
+ * update message requesting assured replication with Safe Read sub-mode.
+ * It also includes info/routines for constructing the final ack to be sent to
+ * the sender of the update message.
+ */
+public class SafeReadExpectedAcksInfo extends ExpectedAcksInfo
+{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+ // Did some servers go in timeout when the matching update was sent ?
+ private boolean hasTimeout = false;
+
+ // Were some servers in wrong status when the matching update was sent ?
+ private boolean hasWrongStatus = false;
+
+ // Did some servers make an error replaying the sent matching update ?
+ private boolean hasReplayError = false;
+
+ // The list of server ids that had errors for the sent matching update
+ // Each server id of the list had one of the
+ // 3 possible errors (timeout, wrong status or replay error)
+ private List<Short> failedServers = null;
+
+ /**
+ * This gives the list of servers we are willing to wait acks from and the
+ * information about the ack from the servers.
+ * key: the id of the server.
+ * value: a boolean true if we received the ack from the server,
+ * false otherwise.
+ * This must not include servers we already identified they are in wrong
+ * status, but just servers that are in normal status.
+ */
+ private Map<Short,Boolean> expectedServersAckStatus =
+ new HashMap<Short,Boolean>();
+
+ /**
+ * Number of servers we want an ack from and from which we received the ack.
+ * Said differently: the number of servers in expectedServersAckStatus whose
+ * value is true. When this value reaches the size of expectedServersAckStatus
+ * we can compute an ack message (based on info in this object), to be
+ * returned to the (requester) server that sent us an assured update message.
+ */
+ private int numKnownAckStatus = 0;
+
+ /**
+ * Creates a new SafeReadExpectedAcksInfo.
+ * @param changeNumber The change number of the assured update message
+ * @param requesterServerHandler The server that sent the assured update
+ * message
+ * @param expectedServers The list of servers we want an ack from (they are
+ * in normal status and have the same group id as us)
+ * @param wrongStatusServers The list of all servers already detected in
+ * wrongStatus (degraded status) to keep trace of the error for the future
+ * returning ack we gonna compute
+ */
+ public SafeReadExpectedAcksInfo(ChangeNumber changeNumber,
+ ServerHandler requesterServerHandler, List<Short> expectedServers,
+ List<Short> wrongStatusServers)
+ {
+ super(changeNumber, requesterServerHandler, AssuredMode.SAFE_READ_MODE);
+
+ // Keep track of potential servers detected in wrong status
+ if (wrongStatusServers.size() > 0)
+ {
+ hasWrongStatus = true;
+ failedServers = wrongStatusServers;
+ }
+
+ // Initialize list of servers we expect acks from
+ for (Short serverId : expectedServers)
+ {
+ expectedServersAckStatus.put(serverId, false);
+ }
+ }
+
+ /**
+ * Sets the timeout marker for the future update ack.
+ * @param hasTimeout True if some timeout occurred
+ */
+ public void setHasTimeout(boolean hasTimeout)
+ {
+ this.hasTimeout = hasTimeout;
+ }
+
+ /**
+ * Sets the wrong status marker for the future update ack.
+ * @param hasWrongStatus True if some servers were in wrong status
+ */
+ public void setHasWrongStatus(boolean hasWrongStatus)
+ {
+ this.hasWrongStatus = hasWrongStatus;
+ }
+
+ /**
+ * Sets the replay error marker for the future update ack.
+ * @param hasReplayError True if some servers had errors replaying the change
+ */
+ public void setHasReplayError(boolean hasReplayError)
+ {
+ this.hasReplayError = hasReplayError;
+ }
+
+ /**
+ * Gets the timeout marker for the future update ack.
+ * @return The timeout marker for the future update ack.
+ */
+ public boolean hasTimeout()
+ {
+ return hasTimeout;
+ }
+
+ /**
+ * Gets the wrong status marker for the future update ack.
+ * @return hasWrongStatus The wrong status marker for the future update ack.
+ */
+ public boolean hasWrongStatus()
+ {
+ return hasWrongStatus;
+ }
+
+ /**
+ * Gets the replay error marker for the future update ack.
+ * @return hasReplayError The replay error marker for the future update ack.
+ */
+ public boolean hasReplayError()
+ {
+ return hasReplayError;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean processReceivedAck(ServerHandler ackingServer, AckMsg ackMsg)
+ {
+ // Get the ack status for the matching server
+ short ackingServerId = ackingServer.getServerId();
+ boolean ackReceived = expectedServersAckStatus.get(ackingServerId);
+ if (ackReceived)
+ {
+ // Sanity check: this should never happen
+ if (debugEnabled())
+ TRACER.debugInfo("Received unexpected ack from server id: "
+ + ackingServerId + " ack message: " + ackMsg);
+ return false;
+ } else
+ {
+ // Analyze received ack and update info for the ack to be later computed
+ // accordingly
+ boolean someErrors = false;
+ if (ackMsg.hasTimeout())
+ {
+ hasTimeout = true;
+ someErrors = true;
+ }
+ if (ackMsg.hasWrongStatus())
+ {
+ hasWrongStatus = true;
+ someErrors = true;
+ }
+ if (ackMsg.hasReplayError())
+ {
+ hasReplayError = true;
+ someErrors = true;
+ }
+ if (someErrors)
+ {
+ failedServers.addAll(ackMsg.getFailedServers());
+ }
+
+ // Mark this ack received for the server
+ expectedServersAckStatus.put(ackingServerId, true);
+ numKnownAckStatus++;
+ }
+
+ return (numKnownAckStatus == expectedServersAckStatus.size());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public AckMsg createAck(boolean timeout)
+ {
+ AckMsg ack = new AckMsg(changeNumber);
+
+ // Fill collected errors info
+ ack.setHasTimeout(hasTimeout);
+ ack.setHasWrongStatus(hasWrongStatus);
+ ack.setHasReplayError(hasReplayError);
+ ack.setFailedServers(failedServers);
+
+ // Force anyway timeout flag if requested
+ if (timeout)
+ ack.setHasTimeout(true);
+
+ return ack;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerHandler.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerHandler.java
index 238a243..df7211a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerHandler.java
@@ -40,7 +40,6 @@
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -67,7 +66,6 @@
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeBuilder;
import org.opends.server.types.Attributes;
-import org.opends.server.types.DN;
import org.opends.server.types.InitializationException;
import org.opends.server.util.TimeThread;
@@ -95,16 +93,12 @@
private ProtocolSession session;
private final MsgQueue msgQueue = new MsgQueue();
private MsgQueue lateQueue = new MsgQueue();
- private final Map<ChangeNumber, AckMessageList> waitingAcks =
- new HashMap<ChangeNumber, AckMessageList>();
private ReplicationServerDomain replicationServerDomain = null;
private String serverURL;
private int outCount = 0; // number of update sent to the server
private int inCount = 0; // number of updates received from the server
- private int inAckCount = 0;
- private int outAckCount = 0;
private int maxReceiveQueue = 0;
private int maxSendQueue = 0;
private int maxReceiveDelay = 0;
@@ -120,7 +114,7 @@
private ServerState serverState;
private boolean activeWriter = true;
private ServerWriter writer = null;
- private DN baseDn = null;
+ private String baseDn = null;
private int rcvWindow;
private int rcvWindowSizeHalf;
private int maxRcvWindow;
@@ -143,7 +137,7 @@
* Properties filled only if remote server is a DS
*/
- // Status of this DS
+ // Status of this DS (only used if this server handler represents a DS)
private ServerStatus status = ServerStatus.INVALID_STATUS;
// Referrals URLs this DS is exporting
private List<String> refUrls = new ArrayList<String>();
@@ -182,9 +176,6 @@
* Set when ServerHandler is stopping.
*/
private AtomicBoolean shuttingDown = new AtomicBoolean(false);
- private static final Map<ChangeNumber, ReplServerAckMessageList>
- changelogsWaitingAcks =
- new HashMap<ChangeNumber, ReplServerAckMessageList>();
/**
* Creates a new server handler instance with the provided socket.
@@ -266,7 +257,7 @@
* @param replicationServer the ReplicationServer that created this server
* handler.
*/
- public void start(DN baseDn, short replicationServerId,
+ public void start(String baseDn, short replicationServerId,
String replicationServerURL,
int windowSize, boolean sslEncryption,
ReplicationServer replicationServer)
@@ -571,7 +562,7 @@
{
// Timeout
Message message = NOTE_TIMEOUT_WHEN_CROSS_CONNECTION.get(
- this.baseDn.toNormalizedString(),
+ this.baseDn,
Short.toString(serverId),
Short.toString(replicationServer.getServerId()));
closeSession(message);
@@ -732,7 +723,7 @@
// gen ID and so won't change without a reset
// then we are just degrading the peer.
Message message = NOTE_BAD_GENERATION_ID_FROM_RS.get(
- this.baseDn.toNormalizedString(),
+ this.baseDn,
Short.toString(serverId),
Long.toString(generationId),
Long.toString(localGenerationId));
@@ -759,7 +750,7 @@
// replicationServerDomain.
// setGenerationId(generationId, false);
Message message = NOTE_BAD_GENERATION_ID_FROM_RS.get(
- this.baseDn.toNormalizedString(),
+ this.baseDn,
Short.toString(serverId),
Long.toString(generationId),
Long.toString(localGenerationId));
@@ -859,7 +850,7 @@
if (generationId != localGenerationId)
{
Message message = NOTE_BAD_GENERATION_ID_FROM_DS.get(
- this.baseDn.toNormalizedString(),
+ this.baseDn,
Short.toString(serverId),
Long.toString(generationId),
Long.toString(localGenerationId));
@@ -873,7 +864,7 @@
// If the LDAP server has already sent changes
// it is not expected to connect to an empty RS
Message message = NOTE_BAD_GENERATION_ID_FROM_DS.get(
- this.baseDn.toNormalizedString(),
+ this.baseDn,
Short.toString(serverId),
Long.toString(generationId),
Long.toString(localGenerationId));
@@ -957,7 +948,7 @@
// gen ID and so won't change without a reset
// then we are just degrading the peer.
Message message = NOTE_BAD_GENERATION_ID_FROM_RS.get(
- this.baseDn.toNormalizedString(),
+ this.baseDn,
Short.toString(serverId),
Long.toString(generationId),
Long.toString(localGenerationId));
@@ -984,7 +975,7 @@
// replicationServerDomain.
// setGenerationId(generationId, false);
Message message = NOTE_BAD_GENERATION_ID_FROM_RS.get(
- this.baseDn.toNormalizedString(),
+ this.baseDn,
Short.toString(serverId),
Long.toString(generationId),
Long.toString(localGenerationId));
@@ -1195,26 +1186,6 @@
}
/**
- * Get the number of Ack received from the server managed by this handler.
- *
- * @return Returns the inAckCount.
- */
- public int getInAckCount()
- {
- return inAckCount;
- }
-
- /**
- * Get the number of Ack sent to the server managed by this handler.
- *
- * @return Returns the outAckCount.
- */
- public int getOutAckCount()
- {
- return outAckCount;
- }
-
- /**
* Check is this server is saturated (this server has already been
* sent a bunch of updates and has not processed them so they are staying
* in the message queue for this server an the size of the queue
@@ -1763,145 +1734,14 @@
}
/**
- * Send the ack to the server that did the original modification.
+ * Sends an ack message to the server represented by this object.
*
- * @param changeNumber The ChangeNumber of the update that is acked.
+ * @param ack The ack message to be sent.
* @throws IOException In case of Exception thrown sending the ack.
*/
- public void sendAck(ChangeNumber changeNumber) throws IOException
+ public void sendAck(AckMsg ack) throws IOException
{
- AckMsg ack = new AckMsg(changeNumber);
session.publish(ack);
- outAckCount++;
- }
-
- /**
- * Do the work when an ack message has been received from another server.
- *
- * @param message The ack message that was received.
- * @param ackingServerId The id of the server that acked the change.
- */
- public void ack(AckMsg message, short ackingServerId)
- {
- ChangeNumber changeNumber = message.getChangeNumber();
- AckMessageList ackList;
- boolean completedFlag;
- synchronized (waitingAcks)
- {
- ackList = waitingAcks.get(changeNumber);
- if (ackList == null)
- return;
- ackList.addAck(ackingServerId);
- completedFlag = ackList.completed();
- if (completedFlag)
- {
- waitingAcks.remove(changeNumber);
- }
- }
- if (completedFlag)
- {
- replicationServerDomain.sendAck(changeNumber, true);
- }
- }
-
- /**
- * Process reception of an for an update that was received from a
- * ReplicationServer.
- *
- * @param message the ack message that was received.
- * @param ackingServerId The id of the server that acked the change.
- */
- public static void ackChangelog(AckMsg message, short ackingServerId)
- {
- ChangeNumber changeNumber = message.getChangeNumber();
- ReplServerAckMessageList ackList;
- boolean completedFlag;
- synchronized (changelogsWaitingAcks)
- {
- ackList = changelogsWaitingAcks.get(changeNumber);
- if (ackList == null)
- return;
- ackList.addAck(ackingServerId);
- completedFlag = ackList.completed();
- if (completedFlag)
- {
- changelogsWaitingAcks.remove(changeNumber);
- }
- }
- if (completedFlag)
- {
- ReplicationServerDomain replicationServerDomain =
- ackList.getChangelogCache();
- replicationServerDomain.sendAck(changeNumber, false,
- ackList.getReplicationServerId());
- }
- }
-
- /**
- * Add an update to the list of update waiting for acks.
- *
- * @param update the update that must be added to the list
- * @param nbWaitedAck The number of ack that must be received before
- * the update is fully acked.
- */
- public void addWaitingAck(UpdateMsg update, int nbWaitedAck)
- {
- AckMessageList ackList = new AckMessageList(update.getChangeNumber(),
- nbWaitedAck);
- synchronized (waitingAcks)
- {
- waitingAcks.put(update.getChangeNumber(), ackList);
- }
- }
-
- /**
- * Add an update to the list of update received from a replicationServer and
- * waiting for acks.
- *
- * @param update The update that must be added to the list.
- * @param ChangelogServerId The identifier of the replicationServer that sent
- * the update.
- * @param replicationServerDomain The ReplicationServerDomain from which the
- * change was processed and to which the ack
- * must later be sent.
- * @param nbWaitedAck The number of ack that must be received before
- * the update is fully acked.
- */
- public static void addWaitingAck(
- UpdateMsg update,
- short ChangelogServerId, ReplicationServerDomain replicationServerDomain,
- int nbWaitedAck)
- {
- ReplServerAckMessageList ackList =
- new ReplServerAckMessageList(update.getChangeNumber(),
- nbWaitedAck,
- ChangelogServerId,
- replicationServerDomain);
- synchronized (changelogsWaitingAcks)
- {
- changelogsWaitingAcks.put(update.getChangeNumber(), ackList);
- }
- }
-
- /**
- * Get the size of the list of update waiting for acks.
- *
- * @return the size of the list of update waiting for acks.
- */
- public int getWaitingAckSize()
- {
- synchronized (waitingAcks)
- {
- return waitingAcks.size();
- }
- }
-
- /**
- * Increment the count of Acks received from this server.
- */
- public void incrementInAckCount()
- {
- inAckCount++;
}
/**
@@ -2001,13 +1841,13 @@
.valueOf(serverId)));
attributes.add(Attributes.create("base-dn", baseDn.toString()));
- if (serverIsLDAPserver)
+ try
{
MonitorData md;
- try
- {
- md = replicationServerDomain.getMonitorData();
+ md = replicationServerDomain.getMonitorData();
+ if (serverIsLDAPserver)
+ {
// Oldest missing update
Long approxFirstMissingDate = md.getApproxFirstMissingDate(serverId);
if ((approxFirstMissingDate != null) && (approxFirstMissingDate > 0))
@@ -2017,7 +1857,7 @@
"approx-older-change-not-synchronized", date.toString()));
attributes.add(Attributes.create(
"approx-older-change-not-synchronized-millis", String
- .valueOf(approxFirstMissingDate)));
+ .valueOf(approxFirstMissingDate)));
}
// Missing changes
@@ -2030,14 +1870,21 @@
attributes.add(Attributes.create("approximate-delay", String
.valueOf(delay)));
}
- catch (Exception e)
+ else
{
- // TODO: improve the log
- // We failed retrieving the remote monitor data.
- attributes.add(Attributes.create("error",
- stackTraceToSingleLineString(e)));
+ // Missing changes
+ long missingChanges = md.getMissingChangesRS(serverId);
+ attributes.add(Attributes.create("missing-changes", String
+ .valueOf(missingChanges)));
}
}
+ catch (Exception e)
+ {
+ // TODO: improve the log
+ // We failed retrieving the remote monitor data.
+ attributes.add(Attributes.create("error",
+ stackTraceToSingleLineString(e)));
+ }
attributes.add(
Attributes.create("queue-size", String.valueOf(msgQueue.count())));
@@ -2056,14 +1903,6 @@
attributes.add(Attributes.create("update-received", String
.valueOf(getInCount())));
- // Deprecated as long as assured is not exposed
- attributes.add(Attributes.create("update-waiting-acks", String
- .valueOf(getWaitingAckSize())));
- attributes.add(Attributes.create("ack-sent", String
- .valueOf(getOutAckCount())));
- attributes.add(Attributes.create("ack-received", String
- .valueOf(getInAckCount())));
-
// Window stats
attributes.add(Attributes.create("max-send-window", String
.valueOf(sendWindowSize)));
@@ -2125,11 +1964,14 @@
/*
* Stop the remote LSHandler
*/
- for (LightweightServerHandler lsh : directoryServers.values())
+ synchronized (directoryServers)
{
- lsh.stopHandler();
+ for (LightweightServerHandler lsh : directoryServers.values())
+ {
+ lsh.stopHandler();
+ }
+ directoryServers.clear();
}
- directoryServers.clear();
/*
* Stop the heartbeat thread.
@@ -2217,7 +2059,6 @@
{
WindowMsg msg = new WindowMsg(rcvWindowSizeHalf);
session.publish(msg);
- outAckCount++;
rcvWindow += rcvWindowSizeHalf;
}
}
@@ -2302,23 +2143,26 @@
*/
List<DSInfo> dsInfos = topoMsg.getDsList();
- // Removes the existing structures
- for (LightweightServerHandler lsh : directoryServers.values())
+ synchronized (directoryServers)
{
- lsh.stopHandler();
- }
- directoryServers.clear();
+ // Removes the existing structures
+ for (LightweightServerHandler lsh : directoryServers.values())
+ {
+ lsh.stopHandler();
+ }
+ directoryServers.clear();
- // Creates the new structure according to the message received.
- for (DSInfo dsInfo : dsInfos)
- {
- LightweightServerHandler lsh = new LightweightServerHandler(this,
- serverId, dsInfo.getDsId(), dsInfo.getGenerationId(),
- dsInfo.getGroupId(), dsInfo.getStatus(), dsInfo.getRefUrls(),
- dsInfo.isAssured(), dsInfo.getAssuredMode(),
- dsInfo.getSafeDataLevel());
- lsh.startHandler();
- directoryServers.put(lsh.getServerId(), lsh);
+ // Creates the new structure according to the message received.
+ for (DSInfo dsInfo : dsInfos)
+ {
+ LightweightServerHandler lsh = new LightweightServerHandler(this,
+ serverId, dsInfo.getDsId(), dsInfo.getGenerationId(),
+ dsInfo.getGroupId(), dsInfo.getStatus(), dsInfo.getRefUrls(),
+ dsInfo.isAssured(), dsInfo.getAssuredMode(),
+ dsInfo.getSafeDataLevel());
+ lsh.startHandler();
+ directoryServers.put(lsh.getServerId(), lsh);
+ }
}
}
@@ -2445,7 +2289,10 @@
*/
public boolean hasRemoteLDAPServers()
{
- return !directoryServers.isEmpty();
+ synchronized (directoryServers)
+ {
+ return !directoryServers.isEmpty();
+ }
}
/**
@@ -2496,7 +2343,6 @@
// TODO also log an error message.
WindowMsg msg = new WindowMsg(rcvWindow);
session.publish(msg);
- outAckCount++;
} else
{
// Both the LDAP server and the replication server believes that the
@@ -2556,17 +2402,10 @@
*/
public Set<Short> getConnectedDirectoryServerIds()
{
- return directoryServers.keySet();
- }
-
- /**
- * Get the map of connected DSs
- * (to the RS represented by this server handler).
- * @return The map of connected DSs
- */
- public Map<Short, LightweightServerHandler> getConnectedDSs()
- {
- return directoryServers;
+ synchronized (directoryServers)
+ {
+ return directoryServers.keySet();
+ }
}
/**
@@ -2716,4 +2555,32 @@
{
return protocolVersion;
}
+
+ /**
+ * Add the DSinfos of the connected Directory Servers
+ * to the List of DSInfo provided as a parameter.
+ *
+ * @param dsInfos The List of DSInfo that should be updated
+ * with the DSInfo for the directoryServers
+ * connected to this ServerHandler.
+ */
+ public void addDSInfos(List<DSInfo> dsInfos)
+ {
+ synchronized (directoryServers)
+ {
+ for (LightweightServerHandler ls : directoryServers.values())
+ {
+ dsInfos.add(ls.toDSInfo());
+ }
+ }
+ }
+
+ /**
+ * Gets the group id of the server represented by this object.
+ * @return The group id of the server represented by this object.
+ */
+ public byte getGroupId()
+ {
+ return groupId;
+ }
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerReader.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerReader.java
index 78c70fa..bea8995 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerReader.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerReader.java
@@ -141,7 +141,7 @@
{
AckMsg ack = (AckMsg) msg;
handler.checkWindow();
- replicationServerDomain.ack(ack, serverId);
+ replicationServerDomain.processAck(ack, handler);
} else if (msg instanceof UpdateMsg)
{
boolean filtered = false;
@@ -171,7 +171,7 @@
logError(ERR_IGNORING_UPDATE_FROM_DS_BADGENID.get(
Short.toString(replicationServerDomain.
getReplicationServer().getServerId()),
- replicationServerDomain.getBaseDn().toNormalizedString(),
+ replicationServerDomain.getBaseDn(),
((UpdateMsg) msg).getChangeNumber().toString(),
Short.toString(handler.getServerId()),
Long.toString(referenceGenerationId),
@@ -180,7 +180,7 @@
logError(ERR_IGNORING_UPDATE_FROM_DS_FULLUP.get(
Short.toString(replicationServerDomain.
getReplicationServer().getServerId()),
- replicationServerDomain.getBaseDn().toNormalizedString(),
+ replicationServerDomain.getBaseDn(),
((UpdateMsg) msg).getChangeNumber().toString(),
Short.toString(handler.getServerId())));
filtered = true;
@@ -199,7 +199,7 @@
logError(ERR_IGNORING_UPDATE_FROM_RS.get(
Short.toString(replicationServerDomain.getReplicationServer().
getServerId()),
- replicationServerDomain.getBaseDn().toNormalizedString(),
+ replicationServerDomain.getBaseDn(),
((UpdateMsg) msg).getChangeNumber().toString(),
Short.toString(handler.getServerId()),
Long.toString(referenceGenerationId),
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerWriter.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerWriter.java
index 7ad2f45..90492e8 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerWriter.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/ServerWriter.java
@@ -138,7 +138,7 @@
logError(ERR_IGNORING_UPDATE_TO_DS_BADGENID.get(
Short.toString(replicationServerDomain.getReplicationServer().
getServerId()),
- replicationServerDomain.getBaseDn().toNormalizedString(),
+ replicationServerDomain.getBaseDn(),
update.getChangeNumber().toString(),
Short.toString(handler.getServerId()),
Long.toString(handler.getGenerationId()),
@@ -147,7 +147,7 @@
logError(ERR_IGNORING_UPDATE_TO_DS_FULLUP.get(
Short.toString(replicationServerDomain.getReplicationServer().
getServerId()),
- replicationServerDomain.getBaseDn().toNormalizedString(),
+ replicationServerDomain.getBaseDn(),
update.getChangeNumber().toString(),
Short.toString(handler.getServerId())));
continue;
@@ -166,7 +166,7 @@
logError(ERR_IGNORING_UPDATE_TO_RS.get(
Short.toString(replicationServerDomain.getReplicationServer().
getServerId()),
- replicationServerDomain.getBaseDn().toNormalizedString(),
+ replicationServerDomain.getBaseDn(),
update.getChangeNumber().toString(),
Short.toString(handler.getServerId()),
Long.toString(handler.getGenerationId()),
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/StatusAnalyzer.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/StatusAnalyzer.java
index 547f23c..0353276 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/StatusAnalyzer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/StatusAnalyzer.java
@@ -75,7 +75,7 @@
int degradedStatusThreshold)
{
super("Replication Server Status Analyzer for " +
- replicationServerDomain.getBaseDn().toNormalizedString() + " in RS " +
+ replicationServerDomain.getBaseDn() + " in RS " +
replicationServerDomain.getReplicationServer().getServerId());
this.replicationServerDomain = replicationServerDomain;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tasks/InitializeTargetTask.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/InitializeTargetTask.java
similarity index 83%
rename from opendj-sdk/opends/src/server/org/opends/server/tasks/InitializeTargetTask.java
rename to opendj-sdk/opends/src/server/org/opends/server/replication/service/InitializeTargetTask.java
index 4882bdf..17c4231 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tasks/InitializeTargetTask.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/InitializeTargetTask.java
@@ -24,7 +24,12 @@
*
* Copyright 2006-2008 Sun Microsystems, Inc.
*/
-package org.opends.server.tasks;
+package org.opends.server.replication.service;
+import org.opends.server.tasks.TaskUtils;
+
+import org.opends.messages.TaskMessages;
+import org.opends.server.types.ResultCode;
+
import org.opends.messages.MessageBuilder;
import static org.opends.server.config.ConfigConstants.*;
@@ -36,18 +41,12 @@
import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskState;
-import org.opends.messages.TaskMessages;
-import org.opends.messages.Message;
-import org.opends.server.replication.plugin.ReplicationDomain;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
-import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
-import org.opends.server.types.ResultCode;
-
/**
* This class provides an implementation of a Directory Server task that can
* be used to import data from an LDIF file into a backend.
@@ -60,22 +59,10 @@
private static final DebugTracer TRACER = getTracer();
// Config properties
- boolean append = false;
- boolean isCompressed = false;
- boolean isEncrypted = false;
- boolean skipSchemaValidation = false;
- String domainString = null;
- ReplicationDomain domain = null;
- short target;
- long total;
- long left;
-
- /**
- * {@inheritDoc}
- */
- public Message getDisplayName() {
- return TaskMessages.INFO_TASK_INITIALIZE_TARGET_NAME.get();
- }
+ private String domainString = null;
+ private ReplicationDomain domain = null;
+ private short target;
+ private long total;
/**
* {@inheritDoc}
@@ -102,20 +89,17 @@
attrList = taskEntry.getAttribute(typeDomainBase);
domainString = TaskUtils.getSingleValueString(attrList);
- DN domainDN = DN.nullDN();
try
{
- domainDN = DN.decode(domainString);
+ domain = ReplicationDomain.retrievesReplicationDomain(domainString);
}
- catch(Exception e)
+ catch(DirectoryException e)
{
MessageBuilder mb = new MessageBuilder();
mb.append(TaskMessages.ERR_TASK_INITIALIZE_INVALID_DN.get());
- mb.append(e.getLocalizedMessage());
- throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
- mb.toMessage());
+ mb.append(e.getMessage());
+ throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, e);
}
- domain=ReplicationDomain.retrievesReplicationDomain(domainDN);
attrList = taskEntry.getAttribute(typeScope);
String targetString = TaskUtils.getSingleValueString(attrList);
@@ -170,7 +154,6 @@
*/
public void setLeft(long left) throws DirectoryException
{
- this.left = left;
replaceAttributeValue(ATTR_TASK_INITIALIZE_LEFT, String.valueOf(left));
replaceAttributeValue(ATTR_TASK_INITIALIZE_DONE,String.valueOf(total-left));
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tasks/InitializeTask.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/InitializeTask.java
similarity index 90%
rename from opendj-sdk/opends/src/server/org/opends/server/tasks/InitializeTask.java
rename to opendj-sdk/opends/src/server/org/opends/server/replication/service/InitializeTask.java
index 88333b2..08c9412 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tasks/InitializeTask.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/InitializeTask.java
@@ -24,10 +24,16 @@
*
* Copyright 2006-2008 Sun Microsystems, Inc.
*/
-package org.opends.server.tasks;
-import org.opends.messages.Message;
+package org.opends.server.replication.service;
+import org.opends.server.tasks.TaskUtils;
+
+import org.opends.server.types.ResultCode;
+
import org.opends.messages.MessageBuilder;
+
+import org.opends.messages.Message;
+
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.core.DirectoryServer.getAttributeType;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
@@ -39,13 +45,10 @@
import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskState;
import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.replication.plugin.ReplicationDomain;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
-import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
-import org.opends.server.types.ResultCode;
/**
* This class provides an implementation of a Directory Server task that can
@@ -59,13 +62,10 @@
*/
private static final DebugTracer TRACER = getTracer();
- boolean isCompressed = false;
- boolean isEncrypted = false;
- boolean skipSchemaValidation = false;
- String domainString = null;
- short source;
- ReplicationDomain domain = null;
- TaskState initState;
+ private String domainString = null;
+ private short source;
+ private ReplicationDomain domain = null;
+ private TaskState initState;
// The total number of entries expected to be processed when this import
// will end successfully
@@ -108,22 +108,19 @@
List<Attribute> attrList;
attrList = taskEntry.getAttribute(typeDomainBase);
domainString = TaskUtils.getSingleValueString(attrList);
- DN domainDN = DN.nullDN();
+
try
{
- domainDN = DN.decode(domainString);
+ domain = ReplicationDomain.retrievesReplicationDomain(domainString);
}
- catch(Exception e)
+ catch(DirectoryException e)
{
MessageBuilder mb = new MessageBuilder();
mb.append(TaskMessages.ERR_TASK_INITIALIZE_INVALID_DN.get());
mb.append(e.getMessage());
- throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
- mb.toMessage());
+ throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, e);
}
- domain = ReplicationDomain.retrievesReplicationDomain(domainDN);
-
attrList = taskEntry.getAttribute(typeSourceScope);
String sourceString = TaskUtils.getSingleValueString(attrList);
source = domain.decodeSource(sourceString);
@@ -140,7 +137,7 @@
if (debugEnabled())
{
TRACER.debugInfo("InitializeTask is starting domain: %s source:%d",
- domain.getBaseDN(), source);
+ domain.getServiceID(), source);
}
initState = getTaskState();
try
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ListenerThread.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ListenerThread.java
similarity index 72%
rename from opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ListenerThread.java
rename to opendj-sdk/opends/src/server/org/opends/server/replication/service/ListenerThread.java
index bb5f214..dd48f8a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ListenerThread.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ListenerThread.java
@@ -24,9 +24,7 @@
*
* Copyright 2006-2008 Sun Microsystems, Inc.
*/
-package org.opends.server.replication.plugin;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
+package org.opends.server.replication.service;
import org.opends.messages.Message;
import static org.opends.server.loggers.ErrorLogger.logError;
@@ -53,23 +51,18 @@
private ReplicationDomain repDomain;
private boolean shutdown = false;
private boolean done = false;
- private LinkedBlockingQueue<UpdateToReplay> updateToReplayQueue;
/**
* Constructor for the ListenerThread.
*
* @param repDomain the replication domain that created this thread
- * @param updateToReplayQueue The update messages queue we must
- * store messages in
*/
- public ListenerThread(ReplicationDomain repDomain,
- LinkedBlockingQueue<UpdateToReplay> updateToReplayQueue)
+ public ListenerThread(ReplicationDomain repDomain)
{
super("Replication Listener for server id " + repDomain.getServerId() +
- " and domain " + repDomain.getBaseDN());
+ " and domain " + repDomain.getServiceID());
this.repDomain = repDomain;
- this.updateToReplayQueue = updateToReplayQueue;
}
/**
@@ -101,24 +94,8 @@
// queue
while ((!shutdown) && ((updateMsg = repDomain.receive()) != null))
{
- // Put update message into the queue (block until some place in the
- // queue is available)
- UpdateToReplay updateToReplay =
- new UpdateToReplay(updateMsg, repDomain);
- boolean queued = false;
- while (!queued && !shutdown)
- {
- // Use timedout method (offer) instead of put for being able to
- // shutdown the thread
- queued = updateToReplayQueue.offer(updateToReplay,
- 1L, TimeUnit.SECONDS);
- }
- if (!queued)
- {
- // Shutdown requested but could not push message: ensure this one is
- // not lost and put it in the queue before dying
- updateToReplayQueue.offer(updateToReplay);
- }
+ if (repDomain.processUpdate(updateMsg) == true)
+ repDomain.processUpdateDoneSynchronous(updateMsg);
}
if (updateMsg == null)
shutdown = true;
@@ -134,9 +111,6 @@
}
}
- // Stop the HeartBeat thread
- repDomain.getBroker().stopHeartBeat();
-
done = true;
if (debugEnabled())
@@ -161,7 +135,7 @@
if (n >= FACTOR)
{
TRACER.debugInfo("Interrupting listener thread for dn " +
- repDomain.getBaseDN() + " in DS " + repDomain.getServerId());
+ repDomain.getServiceID() + " in DS " + repDomain.getServerId());
this.interrupt();
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplLDIFInputStream.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplInputStream.java
similarity index 94%
rename from opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplLDIFInputStream.java
rename to opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplInputStream.java
index 3e66ee9..7e0bd4b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplLDIFInputStream.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplInputStream.java
@@ -24,7 +24,7 @@
*
* Copyright 2006-2008 Sun Microsystems, Inc.
*/
-package org.opends.server.replication.plugin;
+package org.opends.server.replication.service;
@@ -36,7 +36,7 @@
* This class creates an input stream that can be used to read entries generated
* by SynchroLDIF as if they were being read from another source like a file.
*/
-public class ReplLDIFInputStream
+public class ReplInputStream
extends InputStream
{
// Indicates whether this input stream has been closed.
@@ -51,14 +51,14 @@
/**
* Creates a new ReplLDIFInputStream that will import entries
- * for a synchronzation domain.
+ * for a synchronization domain.
*
* @param domain The replication domain
*/
- public ReplLDIFInputStream(ReplicationDomain domain)
+ public ReplInputStream(ReplicationDomain domain)
{
this.domain = domain;
- closed = false;
+ closed = false;
}
/**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplOutputStream.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplOutputStream.java
new file mode 100644
index 0000000..76dcf92
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplOutputStream.java
@@ -0,0 +1,82 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2006-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class creates an output stream that can be used to export entries
+ * to a synchronization domain.
+ */
+public class ReplOutputStream
+ extends OutputStream
+{
+ // The synchronization domain on which the export is done
+ ReplicationDomain domain;
+
+ // The current number of entries exported
+ private long numExportedEntries;
+ String entryBuffer = "";
+
+ /**
+ * Creates a new ReplLDIFOutputStream related to a replication
+ * domain.
+ *
+ * @param domain The replication domain
+ */
+ public ReplOutputStream(ReplicationDomain domain)
+ {
+ this.domain = domain;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void write(int i) throws IOException
+ {
+ throw new IOException("Invalid call");
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void write(byte b[], int off, int len) throws IOException
+ {
+ domain.exportLDIFEntry(b, off, len);
+ }
+
+ /**
+ * Return the number of exported entries.
+ * @return the numExportedEntries
+ */
+ public long getNumExportedEntries() {
+ return numExportedEntries;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplicationBroker.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
similarity index 81%
rename from opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplicationBroker.java
rename to opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
index 27a09c7..16e428d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/ReplicationBroker.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationBroker.java
@@ -24,12 +24,15 @@
*
* Copyright 2006-2008 Sun Microsystems, Inc.
*/
-package org.opends.server.replication.plugin;
+package org.opends.server.replication.service;
+
+import org.opends.server.replication.protocol.HeartbeatMonitor;
import org.opends.messages.*;
import static org.opends.server.loggers.ErrorLogger.logError;
import static org.opends.server.loggers.debug.DebugLogger.*;
+
import org.opends.server.loggers.debug.DebugTracer;
import static org.opends.messages.ReplicationMessages.*;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
@@ -45,34 +48,22 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.LinkedHashSet;
import java.util.List;
-import java.util.TreeSet;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.opends.server.api.DirectoryThread;
-import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.protocols.internal.InternalSearchListener;
-import org.opends.server.protocols.internal.InternalSearchOperation;
-import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.common.DSInfo;
import org.opends.server.replication.common.RSInfo;
import org.opends.server.replication.common.ServerState;
import org.opends.server.replication.common.ServerStatus;
import org.opends.server.replication.protocol.*;
-import org.opends.server.types.DN;
-import org.opends.server.types.DereferencePolicy;
-import org.opends.server.types.ResultCode;
-import org.opends.server.types.SearchResultEntry;
-import org.opends.server.types.SearchResultReference;
-import org.opends.server.types.SearchScope;
/**
* The broker for Multi-master Replication.
*/
-public class ReplicationBroker implements InternalSearchListener
+public class ReplicationBroker
{
/**
@@ -83,23 +74,17 @@
private Collection<String> servers;
private boolean connected = false;
private String replicationServer = "Not connected";
- private TreeSet<FakeOperation> replayOperations;
private ProtocolSession session = null;
private final ServerState state;
- private final DN baseDn;
+ private final String baseDn;
private final short serverId;
- private int maxSendDelay;
- private int maxReceiveDelay;
- private int maxSendQueue;
- private int maxReceiveQueue;
private Semaphore sendWindow;
private int maxSendWindow;
- private int rcvWindow;
- private int halfRcvWindow;
- private int maxRcvWindow;
+ private int rcvWindow = 100;
+ private int halfRcvWindow = rcvWindow/2;
+ private int maxRcvWindow = rcvWindow;
private int timeout = 0;
private short protocolVersion;
- private long generationId = -1;
private ReplSessionSecurity replSessionSecurity;
// My group id
private byte groupId = (byte) -1;
@@ -110,7 +95,7 @@
// The server URL of the RS we are connected to
private String rsServerUrl = null;
// Our replication domain
- private ReplicationDomain replicationDomain = null;
+ private ReplicationDomain domain = null;
// Trick for avoiding a inner class for many parameters return for
// performPhaseOneHandshake method.
@@ -142,6 +127,17 @@
// Same group id poller thread
private SameGroupIdPoller sameGroupIdPoller = null;
+ /*
+ * Properties for the last topology info received from the network.
+ */
+ // Info for other DSs.
+ // Warning: does not contain info for us (for our server id)
+ private List<DSInfo> dsList = new ArrayList<DSInfo>();
+ // Info for other RSs.
+ private List<RSInfo> rsList = new ArrayList<RSInfo>();
+
+ private long generationID;
+
/**
* Creates a new ReplicationServer Broker for a particular ReplicationDomain.
*
@@ -152,13 +148,6 @@
* when negotiating the session with the replicationServer.
* @param serverId The server ID that should be used by this broker
* when negotiating the session with the replicationServer.
- * @param maxReceiveQueue The maximum size of the receive queue to use on
- * the replicationServer.
- * @param maxReceiveDelay The maximum replication delay to use on the
- * replicationServer.
- * @param maxSendQueue The maximum size of the send queue to use on
- * the replicationServer.
- * @param maxSendDelay The maximum send delay to use on the replicationServer.
* @param window The size of the send and receive window to use.
* @param heartbeatInterval The interval between heartbeats requested of the
* replicationServer, or zero if no heartbeats are requested.
@@ -169,29 +158,32 @@
* @param groupId The group id of our domain.
*/
public ReplicationBroker(ReplicationDomain replicationDomain,
- ServerState state, DN baseDn, short serverId, int maxReceiveQueue,
- int maxReceiveDelay, int maxSendQueue, int maxSendDelay, int window,
- long heartbeatInterval, long generationId,
+ ServerState state, String baseDn, short serverId, int window,
+ long generationId, long heartbeatInterval,
ReplSessionSecurity replSessionSecurity, byte groupId)
{
- this.replicationDomain = replicationDomain;
+ this.domain = replicationDomain;
this.baseDn = baseDn;
this.serverId = serverId;
- this.maxReceiveDelay = maxReceiveDelay;
- this.maxSendDelay = maxSendDelay;
- this.maxReceiveQueue = maxReceiveQueue;
- this.maxSendQueue = maxSendQueue;
this.state = state;
- replayOperations =
- new TreeSet<FakeOperation>(new FakeOperationComparator());
- this.rcvWindow = window;
- this.maxRcvWindow = window;
- this.halfRcvWindow = window / 2;
- this.heartbeatInterval = heartbeatInterval;
this.protocolVersion = ProtocolVersion.getCurrentVersion();
- this.generationId = generationId;
this.replSessionSecurity = replSessionSecurity;
this.groupId = groupId;
+ this.generationID = generationId;
+ this.heartbeatInterval = heartbeatInterval;
+ this.maxRcvWindow = window;
+ this.maxRcvWindow = window;
+ this.halfRcvWindow = window /2;
+ }
+
+ /**
+ * Start the ReplicationBroker.
+ */
+ public void start()
+ {
+ shutdown = false;
+ this.rcvWindow = this.maxRcvWindow;
+ this.connect();
}
/**
@@ -207,6 +199,7 @@
*/
shutdown = false;
this.servers = servers;
+
if (servers.size() < 1)
{
Message message = NOTE_NEED_MORE_THAN_ONE_CHANGELOG_SERVER.get();
@@ -245,6 +238,18 @@
}
/**
+ * Gets the server id.
+ * @return The server id
+ */
+ private long getGenerationID()
+ {
+ if (domain != null)
+ return domain.getGenerationID();
+ else
+ return generationID;
+ }
+
+ /**
* Gets the server url of the RS we are connected to.
* @return The server url of the RS we are connected to
*/
@@ -324,12 +329,12 @@
// May have created a broker with null replication domain for
// unit test purpose.
- if (replicationDomain != null)
+ if (domain != null)
{
// If a first connect or a connection failure occur, we go through here.
// force status machine to NOT_CONNECTED_STATUS so that monitoring can
// see that we are not connected.
- replicationDomain.toNotConnectedStatus();
+ domain.toNotConnectedStatus();
}
// Stop any existing poller and heartbeat monitor from a previous session.
@@ -380,7 +385,8 @@
ServerStatus initStatus =
computeInitialServerStatus(replServerStartMsg.getGenerationId(),
bestServerInfo.getServerState(),
- replServerStartMsg.getDegradedStatusThreshold(), generationId);
+ replServerStartMsg.getDegradedStatusThreshold(),
+ this.getGenerationID());
// Perfom session start (handshake phase 2)
TopologyMsg topologyMsg = performPhaseTwoHandshake(bestServer,
@@ -414,81 +420,6 @@
if ((tmpRsGroupId == groupId) ||
((tmpRsGroupId != groupId) && !someServersWithSameGroupId))
{
- /*
- * We must not publish changes to a replicationServer that has
- * not seen all our previous changes because this could cause
- * some other ldap servers to miss those changes.
- * Check that the ReplicationServer has seen all our previous
- * changes.
- */
- ChangeNumber replServerMaxChangeNumber =
- replServerStartMsg.getServerState().
- getMaxChangeNumber(serverId);
-
- if (replServerMaxChangeNumber == null)
- {
- replServerMaxChangeNumber = new ChangeNumber(0, 0, serverId);
- }
- ChangeNumber ourMaxChangeNumber =
- state.getMaxChangeNumber(serverId);
-
- if ((ourMaxChangeNumber != null) &&
- (!ourMaxChangeNumber.olderOrEqual(replServerMaxChangeNumber)))
- {
-
- // Replication server is missing some of our changes: let's
- // send them to him.
-
- Message message = DEBUG_GOING_TO_SEARCH_FOR_CHANGES.get();
- logError(message);
-
- /*
- * Get all the changes that have not been seen by this
- * replication server and populate the replayOperations
- * list.
- */
- InternalSearchOperation op = searchForChangedEntries(
- baseDn, replServerMaxChangeNumber, this);
- if (op.getResultCode() != ResultCode.SUCCESS)
- {
- /*
- * An error happened trying to search for the updates
- * This server will start acepting again new updates but
- * some inconsistencies will stay between servers.
- * Log an error for the repair tool
- * that will need to resynchronize the servers.
- */
- message = ERR_CANNOT_RECOVER_CHANGES.get(
- baseDn.toNormalizedString());
- logError(message);
- } else
- {
- for (FakeOperation replayOp : replayOperations)
- {
- ChangeNumber cn = replayOp.getChangeNumber();
- /*
- * Because the entry returned by the search operation
- * can contain old historical information, it is
- * possible that some of the FakeOperation are
- * actually older than the
- * Only send the Operation if it was newer than
- * the last ChangeNumber known by the Replication Server.
- */
- if (cn.newer(replServerMaxChangeNumber))
- {
- message =
- DEBUG_SENDING_CHANGE.get(
- replayOp.getChangeNumber().toString());
- logError(message);
- session.publish(replayOp.generateMessage());
- }
- }
- message = DEBUG_CHANGES_SENT.get();
- logError(message);
- }
- replayOperations.clear();
- }
-
replicationServer = tmpReadableServerName;
maxSendWindow = replServerStartMsg.getWindowSize();
rsGroupId = replServerStartMsg.getGroupId();
@@ -497,11 +428,13 @@
// May have created a broker with null replication domain for
// unit test purpose.
- if (replicationDomain != null)
+ if (domain != null)
{
- replicationDomain.setInitialStatus(initStatus);
- replicationDomain.receiveTopo(topologyMsg);
+ domain.sessionInitiated(
+ initStatus, replServerStartMsg.getServerState(),
+ session);
}
+ receiveTopo(topologyMsg);
connected = true;
if (getRsGroupId() != groupId)
{
@@ -528,16 +461,10 @@
// Do not log connection error
newServerWithSameGroupId = true;
}
- } catch (IOException e)
- {
- Message message = ERR_PUBLISHING_FAKE_OPS.get(
- baseDn.toNormalizedString(), bestServer,
- e.getLocalizedMessage() + stackTraceToSingleLineString(e));
- logError(message);
} catch (Exception e)
{
Message message = ERR_COMPUTING_FAKE_OPS.get(
- baseDn.toNormalizedString(), bestServer,
+ baseDn, bestServer,
e.getLocalizedMessage() + stackTraceToSingleLineString(e));
logError(message);
} finally
@@ -577,7 +504,7 @@
this.sendWindow = new Semaphore(maxSendWindow);
connectPhaseLock.notify();
- if ((replServerStartMsg.getGenerationId() == this.generationId) ||
+ if ((replServerStartMsg.getGenerationId() == this.getGenerationID()) ||
(replServerStartMsg.getGenerationId() == -1))
{
Message message =
@@ -586,7 +513,7 @@
Short.toString(rsServerId),
replicationServer,
Short.toString(serverId),
- Long.toString(this.generationId));
+ Long.toString(this.getGenerationID()));
logError(message);
} else
{
@@ -594,7 +521,7 @@
NOTE_NOW_FOUND_BAD_GENERATION_CHANGELOG.get(
baseDn.toString(),
replicationServer,
- Long.toString(this.generationId),
+ Long.toString(this.getGenerationID()),
Long.toString(replServerStartMsg.getGenerationId()));
logError(message);
}
@@ -664,7 +591,7 @@
if (debugEnabled())
{
- TRACER.debugInfo("RB for dn " + baseDn.toNormalizedString() +
+ TRACER.debugInfo("RB for dn " + baseDn +
" and with server id " + Short.toString(serverId) + " computed " +
Integer.toString(nChanges) + " changes late.");
}
@@ -744,10 +671,11 @@
/*
* Send our ServerStartMsg.
*/
- ServerStartMsg serverStartMsg = new ServerStartMsg(serverId, baseDn,
- maxReceiveDelay, maxReceiveQueue, maxSendDelay, maxSendQueue,
- halfRcvWindow * 2, heartbeatInterval, state,
- ProtocolVersion.getCurrentVersion(), generationId, isSslEncryption,
+ ServerStartMsg serverStartMsg = new ServerStartMsg(serverId,
+ baseDn, 0, 0, 0, 0,
+ maxRcvWindow, heartbeatInterval, state,
+ ProtocolVersion.getCurrentVersion(), this.getGenerationID(),
+ isSslEncryption,
groupId);
localSession.publish(serverStartMsg);
@@ -764,11 +692,11 @@
}
// Sanity check
- DN repDn = replServerStartMsg.getBaseDn();
+ String repDn = replServerStartMsg.getBaseDn();
if (!(this.baseDn.equals(repDn)))
{
Message message = ERR_DS_DN_DOES_NOT_MATCH.get(repDn.toString(),
- this.baseDn.toString());
+ this.baseDn);
logError(message);
error = true;
}
@@ -811,10 +739,10 @@
if ( (e instanceof SocketTimeoutException) && debugEnabled() )
{
TRACER.debugInfo("Timeout trying to connect to RS " + server +
- " for dn: " + baseDn.toNormalizedString());
+ " for dn: " + baseDn);
}
Message message = ERR_EXCEPTION_STARTING_SESSION_PHASE.get("1",
- baseDn.toNormalizedString(), server, e.getLocalizedMessage() +
+ baseDn, server, e.getLocalizedMessage() +
stackTraceToSingleLineString(e));
if (keepConnection) // Log error message only for final connection
{
@@ -880,13 +808,15 @@
StartSessionMsg startSessionMsg = null;
// May have created a broker with null replication domain for
// unit test purpose.
- if (replicationDomain != null)
+ if (domain != null)
{
- startSessionMsg = new StartSessionMsg(initStatus,
- replicationDomain.getRefUrls(),
- replicationDomain.isAssured(),
- replicationDomain.getAssuredMode(),
- replicationDomain.getAssuredSdLevel());
+ startSessionMsg =
+ new StartSessionMsg(
+ initStatus,
+ domain.getRefUrls(),
+ domain.isAssured(),
+ domain.getAssuredMode(),
+ domain.getAssuredSdLevel());
} else
{
startSessionMsg =
@@ -912,7 +842,7 @@
} catch (Exception e)
{
Message message = ERR_EXCEPTION_STARTING_SESSION_PHASE.get("2",
- baseDn.toNormalizedString(), server, e.getLocalizedMessage() +
+ baseDn, server, e.getLocalizedMessage() +
stackTraceToSingleLineString(e));
logError(message);
@@ -950,7 +880,7 @@
* @return The computed best replication server.
*/
public static String computeBestReplicationServer(ServerState myState,
- HashMap<String, ServerInfo> rsInfos, short serverId, DN baseDn,
+ HashMap<String, ServerInfo> rsInfos, short serverId, String baseDn,
byte groupId)
{
/*
@@ -997,7 +927,7 @@
* @return The computed best replication server.
*/
private static String searchForBestReplicationServer(ServerState myState,
- HashMap<String, ServerInfo> rsInfos, short serverId, DN baseDn)
+ HashMap<String, ServerInfo> rsInfos, short serverId, String baseDn)
{
/*
* Find replication servers who are up to date (or more up to date than us,
@@ -1072,9 +1002,7 @@
*/
Message message = NOTE_FOUND_CHANGELOGS_WITH_MY_CHANGES.get(
- upToDateServers.size(),
- baseDn.toNormalizedString(),
- Short.toString(serverId));
+ upToDateServers.size(), baseDn, Short.toString(serverId));
logError(message);
/*
@@ -1154,7 +1082,7 @@
*/
// lateOnes cannot be empty
Message message = NOTE_COULD_NOT_FIND_CHANGELOG_WITH_MY_CHANGES.get(
- baseDn.toNormalizedString(), lateOnes.size());
+ baseDn, lateOnes.size());
logError(message);
// Min of the shifts
@@ -1190,48 +1118,6 @@
}
/**
- * Search for the changes that happened since fromChangeNumber
- * based on the historical attribute. The only changes that will
- * be send will be the one generated on the serverId provided in
- * fromChangeNumber.
- * @param baseDn the base DN
- * @param fromChangeNumber The change number from which we want the changes
- * @param resultListener that will process the entries returned.
- * @return the internal search operation
- * @throws Exception when raised.
- */
- public static InternalSearchOperation searchForChangedEntries(
- DN baseDn,
- ChangeNumber fromChangeNumber,
- InternalSearchListener resultListener)
- throws Exception
- {
- InternalClientConnection conn =
- InternalClientConnection.getRootConnection();
- Short serverId = fromChangeNumber.getServerId();
-
- String maxValueForId = "ffffffffffffffff" +
- String.format("%04x", serverId) + "ffffffff";
-
- LDAPFilter filter = LDAPFilter.decode(
- "(&(" + Historical.HISTORICALATTRIBUTENAME + ">=dummy:"
- + fromChangeNumber + ")(" + Historical.HISTORICALATTRIBUTENAME +
- "<=dummy:" + maxValueForId + "))");
-
- LinkedHashSet<String> attrs = new LinkedHashSet<String>(1);
- attrs.add(Historical.HISTORICALATTRIBUTENAME);
- attrs.add(Historical.ENTRYUIDNAME);
- attrs.add("*");
- return conn.processSearch(
- new ASN1OctetString(baseDn.toString()),
- SearchScope.WHOLE_SUBTREE,
- DereferencePolicy.NEVER_DEREF_ALIASES,
- 0, 0, false, filter,
- attrs,
- resultListener);
- }
-
- /**
* Start the heartbeat monitor thread.
*/
private void startHeartBeat()
@@ -1325,7 +1211,7 @@
{
MessageBuilder mb = new MessageBuilder();
mb.append(NOTE_EXCEPTION_RESTARTING_SESSION.get(
- baseDn.toNormalizedString(), e.getLocalizedMessage()));
+ baseDn, e.getLocalizedMessage()));
mb.append(stackTraceToSingleLineString(e));
logError(mb.toMessage());
}
@@ -1413,7 +1299,7 @@
}
}
}
- if (!credit)
+ if ((!credit) && (currentWindowSemaphore.availablePermits() == 0))
{
// the window is still closed.
// Send a WindowProbeMsg message to wakeup the receiver in case the
@@ -1436,7 +1322,7 @@
if (debugEnabled())
{
debugInfo("ReplicationBroker.publish() " +
- "IO exception raised : " + e.getLocalizedMessage());
+ "Interrupted exception raised : " + e.getLocalizedMessage());
}
}
}
@@ -1479,7 +1365,13 @@
{
WindowMsg windowMsg = (WindowMsg) msg;
sendWindow.release(windowMsg.getNumAck());
- } else
+ }
+ else if (msg instanceof TopologyMsg)
+ {
+ TopologyMsg topoMsg = (TopologyMsg)msg;
+ receiveTopo(topoMsg);
+ }
+ else
{
return msg;
}
@@ -1580,20 +1472,6 @@
}
/**
- * Set the value of the generationId for that broker. Normally the
- * generationId is set through the constructor but there are cases
- * where the value of the generationId must be changed while the broker
- * already exist for example after an on-line import.
- *
- * @param generationId The value of the generationId.
- *
- */
- public void setGenerationId(long generationId)
- {
- this.generationId = generationId;
- }
-
- /**
* Get the name of the replicationServer to which this broker is currently
* connected.
*
@@ -1606,39 +1484,6 @@
}
/**
- * {@inheritDoc}
- */
- public void handleInternalSearchEntry(
- InternalSearchOperation searchOperation,
- SearchResultEntry searchEntry)
- {
- /*
- * This call back is called at session establishment phase
- * for each entry that has been changed by this server and the changes
- * have not been sent to any Replication Server.
- * The role of this method is to build equivalent operation from
- * the historical information and add them in the replayOperations
- * table.
- */
- Iterable<FakeOperation> updates =
- Historical.generateFakeOperations(searchEntry);
- for (FakeOperation op : updates)
- {
- replayOperations.add(op);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public void handleInternalSearchReference(
- InternalSearchOperation searchOperation,
- SearchResultReference searchReference)
- {
- // TODO to be implemented
- }
-
- /**
* Get the maximum receive window size.
*
* @return The maximum receive window size.
@@ -1694,31 +1539,41 @@
}
/**
- * Change some config parameters.
+ * Change some configuration parameters.
*
- * @param replicationServers The new list of replication servers.
- * @param maxReceiveQueue The max size of receive queue.
- * @param maxReceiveDelay The max receive delay.
- * @param maxSendQueue The max send queue.
- * @param maxSendDelay The max Send Delay.
+ * @param replicationServers The new list of replication servers.
* @param window The max window size.
- * @param heartbeatInterval The heartbeat interval.
+ * @param heartbeatInterval The heartBeat interval.
+ *
+ * @return A boolean indicating if the changes
+ * requires to restart the service.
*/
- public void changeConfig(Collection<String> replicationServers,
- int maxReceiveQueue, int maxReceiveDelay, int maxSendQueue,
- int maxSendDelay, int window, long heartbeatInterval)
+ public boolean changeConfig(
+ Collection<String> replicationServers, int window, long heartbeatInterval)
{
- this.servers = replicationServers;
- this.maxRcvWindow = window;
- this.heartbeatInterval = heartbeatInterval;
- this.maxReceiveDelay = maxReceiveDelay;
- this.maxReceiveQueue = maxReceiveQueue;
- this.maxSendDelay = maxSendDelay;
- this.maxSendQueue = maxSendQueue;
+ // These parameters needs to be renegociated with the ReplicationServer
+ // so if they have changed, that requires restarting the session with
+ // the ReplicationServer.
+ Boolean needToRestartSession = false;
- // For info, a new session with the replicationServer
- // will be recreated in the replication domain
- // to take into account the new configuration.
+ // A new session is necessary only when information regarding
+ // the connection is modified
+ if ((servers == null) ||
+ (!(replicationServers.size() == servers.size()
+ && replicationServers.containsAll(servers))) ||
+ window != this.maxRcvWindow ||
+ heartbeatInterval != this.heartbeatInterval)
+ {
+ needToRestartSession = true;
+ }
+
+ this.servers = replicationServers;
+ this.rcvWindow = window;
+ this.maxRcvWindow = window;
+ this.halfRcvWindow = window / 2;
+ this.heartbeatInterval = heartbeatInterval;
+
+ return needToRestartSession;
}
/**
@@ -1900,14 +1755,13 @@
{
try
{
-
ChangeStatusMsg csMsg = new ChangeStatusMsg(ServerStatus.INVALID_STATUS,
newStatus);
session.publish(csMsg);
} catch (IOException ex)
{
Message message = ERR_EXCEPTION_SENDING_CS.get(
- baseDn.toNormalizedString(),
+ baseDn,
Short.toString(serverId),
ex.getLocalizedMessage() + stackTraceToSingleLineString(ex));
logError(message);
@@ -1922,4 +1776,48 @@
{
this.groupId = groupId;
}
+
+ /**
+ * Gets the info for DSs in the topology (except us).
+ * @return The info for DSs in the topology (except us)
+ */
+ public List<DSInfo> getDsList()
+ {
+ return dsList;
+ }
+
+ /**
+ * Gets the info for RSs in the topology (except the one we are connected
+ * to).
+ * @return The info for RSs in the topology (except the one we are connected
+ * to)
+ */
+ public List<RSInfo> getRsList()
+ {
+ return rsList;
+ }
+
+ /**
+ * Processes an incoming TopologyMsg.
+ * Updates the structures for the local view of the topology.
+ *
+ * @param topoMsg The topology information received from RS.
+ */
+ public void receiveTopo(TopologyMsg topoMsg)
+ {
+
+ if (debugEnabled())
+ TRACER.debugInfo("Replication domain " + baseDn
+ + " received topology info update:\n" + topoMsg);
+
+ // Store new lists
+ synchronized(getDsList())
+ {
+ synchronized(getRsList())
+ {
+ dsList = topoMsg.getDsList();
+ rsList = topoMsg.getRsList();
+ }
+ }
+ }
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java
new file mode 100644
index 0000000..9586e00
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationDomain.java
@@ -0,0 +1,2433 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+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.replication.common.StatusMachine.*;
+
+import org.opends.server.replication.common.ChangeNumberGenerator;
+
+import java.io.BufferedOutputStream;
+
+import org.opends.server.types.Attribute;
+
+import org.opends.server.core.DirectoryServer;
+
+
+import java.util.Set;
+
+import org.opends.server.replication.common.DSInfo;
+import org.opends.server.replication.common.RSInfo;
+
+import java.util.HashMap;
+
+import java.util.Map;
+
+import org.opends.server.config.ConfigException;
+import java.util.Collection;
+import org.opends.server.replication.protocol.ReplSessionSecurity;
+import org.opends.server.replication.protocol.ResetGenerationIdMsg;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+import org.opends.server.api.DirectoryThread;
+import org.opends.server.backends.task.Task;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.ChangeNumber;
+import org.opends.server.replication.common.ServerState;
+import org.opends.server.replication.common.ServerStatus;
+import org.opends.server.replication.common.StatusMachine;
+import org.opends.server.replication.common.StatusMachineEvent;
+import org.opends.server.replication.protocol.AckMsg;
+import org.opends.server.replication.protocol.ChangeStatusMsg;
+import org.opends.server.replication.protocol.DoneMsg;
+import org.opends.server.replication.protocol.EntryMsg;
+import org.opends.server.replication.protocol.ErrorMsg;
+import org.opends.server.replication.protocol.HeartbeatMsg;
+import org.opends.server.replication.protocol.InitializeRequestMsg;
+import org.opends.server.replication.protocol.InitializeTargetMsg;
+import org.opends.server.replication.protocol.ProtocolSession;
+import org.opends.server.replication.protocol.ProtocolVersion;
+import org.opends.server.replication.protocol.ReplicationMsg;
+import org.opends.server.replication.protocol.RoutableMsg;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.ResultCode;
+
+/**
+ * This class should be used as a base for Replication implementations.
+ * <p>
+ * It is intended that developer in need of a replication mechanism
+ * subclass this class with their own implementation.
+ * <p>
+ * The startup phase of the ReplicationDomain subclass,
+ * should read the list of replication servers from the configuration,
+ * instantiate a {@link ServerState} then start the publish service
+ * by calling
+ * {@link #startPublishService(Collection, ServerState, int, long)}.
+ * At this point it can start calling the {@link #publish(UpdateMsg)}
+ * method if needed.
+ * <p>
+ * When the startup phase reach the point when the subclass is ready
+ * to handle updates the Replication Domain implementation should call the
+ * {@link #startListenService()} method.
+ * At this point a Listener thread is created on the Replication Service
+ * and which can start receiving updates.
+ * <p>
+ * When updates are received the Replication Service calls the
+ * {@link #processUpdate(UpdateMsg)} method.
+ * ReplicationDomain implementation should implement the appropriate code
+ * for replaying the update on the local repository.
+ * When fully done the subclass must call the
+ * {@link #processUpdateDone(UpdateMsg)} method.
+ * This allows to process the update asynchronously if necessary.
+ *
+ * <p>
+ * To propagate changes to other replica, a ReplicationDomain implementation
+ * must use the {@link #publish(UpdateMsg)} method.
+ * <p>
+ * If the Full Initialization process is needed then implementation
+ * for {@link #importBackend(InputStream)} and
+ * {@link #exportBackend(OutputStream)} must be
+ * provided.
+ * <p>
+ * Full Initialization of a replica can be triggered by LDAP clients
+ * by creating InitializeTasks or InitializeTargetTask.
+ * Full initialization can also by triggered from the ReplicationDomain
+ * implementation using methods {@link #initializeRemote(short, Task)}
+ * or {@link #initializeFromRemote(short, Task)}.
+ * <p>
+ * At shutdown time, the {@link #stopDomain()} method should be called to
+ * cleanly stop the replication service.
+ */
+public abstract class ReplicationDomain
+{
+ /**
+ * Current status for this replicated domain.
+ */
+ private ServerStatus status = ServerStatus.NOT_CONNECTED_STATUS;
+
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+ /**
+ * An identifier for the Replication Service.
+ * All Replication Domain using this identifier will be connected
+ * through the Replication Service.
+ */
+ private final String serviceID;
+
+ /**
+ * The identifier of this Replication Domain inside the
+ * Replication Service.
+ * Each Domain must use a unique ServerID.
+ */
+ private final short serverID;
+
+ /**
+ * The ReplicationBroker that is used by this ReplicationDomain to
+ * connect to the ReplicationService.
+ */
+ private ReplicationBroker broker;
+
+ /**
+ * This Map is used to store all outgoing assured messages in order
+ * to be able to correlate all the coming back acks to the original
+ * operation.
+ */
+ private final SortedMap<ChangeNumber, UpdateMsg> waitingAckMsgs =
+ new TreeMap<ChangeNumber, UpdateMsg>();
+
+
+ /**
+ * The context related to an import or export being processed
+ * Null when none is being processed.
+ */
+ private IEContext ieContext = null;
+
+ /**
+ * The Thread waiting for incoming update messages for this domain and pushing
+ * them to the global incoming update message queue for later processing by
+ * replay threads.
+ */
+ private ListenerThread listenerThread;
+
+ /**
+ * A Map used to store all the ReplicationDomains created on this server.
+ */
+ private static Map<String, ReplicationDomain> domains =
+ new HashMap<String, ReplicationDomain>();
+
+ /**
+ * The Monitor in charge of replication monitoring.
+ */
+ private ReplicationMonitor monitor;
+
+ /*
+ * Assured mode properties
+ */
+ // Is assured mode enabled or not for this domain ?
+ private boolean assured = false;
+ // Assured sub mode (used when assured is true)
+ private AssuredMode assuredMode = AssuredMode.SAFE_DATA_MODE;
+ // Safe Data level (used when assuredMode is SAFE_DATA)
+ private byte assuredSdLevel = (byte)1;
+ // The timeout in ms that should be used, when waiting for assured acks
+ private long assuredTimeout = 1000;
+
+ // Group id
+ private byte groupId = (byte)1;
+ // Referrals urls to be published to other servers of the topology
+ // TODO: fill that with all currently opened urls if no urls configured
+ private List<String> refUrls = new ArrayList<String>();
+
+ /**
+ * A set of counters used for Monitoring.
+ */
+ private AtomicInteger numProcessedUpdates = new AtomicInteger();
+ private AtomicInteger numRcvdUpdates = new AtomicInteger(0);
+ private AtomicInteger numSentUpdates = new AtomicInteger(0);
+
+ /* Assured replication monitoring counters */
+
+ // Number of updates sent in Assured Mode, Safe Read
+ private AtomicInteger assuredSrSentUpdates = new AtomicInteger(0);
+ // Number of updates sent in Assured Mode, Safe Read, that have been
+ // successfully acknowledged
+ private AtomicInteger assuredSrAcknowledgedUpdates = new AtomicInteger(0);
+ // Number of updates sent in Assured Mode, Safe Read, that have not been
+ // successfully acknowledged (either because of timeout, wrong status or error
+ // at replay)
+ private AtomicInteger assuredSrNotAcknowledgedUpdates =
+ new AtomicInteger(0);
+ // Number of updates sent in Assured Mode, Safe Read, that have not been
+ // successfully acknowledged because of timeout
+ private AtomicInteger assuredSrTimeoutUpdates = new AtomicInteger(0);
+ // Number of updates sent in Assured Mode, Safe Read, that have not been
+ // successfully acknowledged because of wrong status
+ private AtomicInteger assuredSrWrongStatusUpdates = new AtomicInteger(0);
+ // Number of updates sent in Assured Mode, Safe Read, that have not been
+ // successfully acknowledged because of replay error
+ private AtomicInteger assuredSrReplayErrorUpdates = new AtomicInteger(0);
+ // Multiple values allowed: number of updates sent in Assured Mode, Safe Read,
+ // that have not been successfully acknowledged (either because of timeout,
+ // wrong status or error at replay) for a particular server (DS or RS). String
+ // format: <server id>:<number of failed updates>
+ private Map<Short,Integer> assuredSrServerNotAcknowledgedUpdates =
+ new HashMap<Short,Integer>();
+ // Number of updates sent in Assured Mode, Safe Data
+ private AtomicInteger assuredSdSentUpdates = new AtomicInteger(0);
+ // Number of updates sent in Assured Mode, Safe Data, that have been
+ // successfully acknowledged
+ private AtomicInteger assuredSdAcknowledgedUpdates = new AtomicInteger(0);
+ // Number of updates sent in Assured Mode, Safe Data, that have not been
+ // successfully acknowledged because of timeout
+ private AtomicInteger assuredSdTimeoutUpdates = new AtomicInteger(0);
+ // Multiple values allowed: number of updates sent in Assured Mode, Safe Data,
+ // that have not been successfully acknowledged because of timeout for a
+ // particular RS. String format: <server id>:<number of failed updates>
+ private Map<Short,Integer> assuredSdServerTimeoutUpdates =
+ new HashMap<Short,Integer>();
+
+ /* Status related monitoring fields */
+
+ // Indicates the date when the status changed. This may be used to indicate
+ // the date the session with the current replication server started (when
+ // status is NORMAL for instance). All the above assured monitoring fields
+ // are also resetted each time the status is changed
+ private Date lastStatusChangeDate = new Date();
+
+ /**
+ * The state maintained by the Concrete Class.
+ */
+ private final ServerState state;
+
+ /**
+ * The generator that will be used to generate {@link ChangeNumber}
+ * for this domain.
+ */
+ private final ChangeNumberGenerator generator;
+
+ /**
+ * Returns the {@link ChangeNumberGenerator} that will be used to
+ * generate {@link ChangeNumber} for this domain.
+ *
+ * @return The {@link ChangeNumberGenerator} that will be used to
+ * generate {@link ChangeNumber} for this domain.
+ */
+ public ChangeNumberGenerator getGenerator()
+ {
+ return generator;
+ }
+
+ /**
+ * Creates a ReplicationDomain with the provided parameters.
+ *
+ * @param serviceID The identifier of the Replication Domain to which
+ * this object is participating.
+ * @param serverID The identifier of the server that is participating
+ * to the Replication Domain.
+ * This identifier should be different for each server that
+ * is participating to a given Replication Domain.
+ */
+ public ReplicationDomain(String serviceID, short serverID)
+ {
+ this.serviceID = serviceID;
+ this.serverID = serverID;
+ this.state = new ServerState();
+ this.generator = new ChangeNumberGenerator(serverID, state);
+
+ domains.put(serviceID, this);
+ }
+
+ /**
+ * Set the initial status of the domain and perform necessary initializations.
+ * This method will be called by the Broker each time the ReplicationBroker
+ * establish a new session to a Replication Server.
+ *
+ * Implementations may override this method when they need to perform
+ * additional computing after session establishment.
+ * The default implementation should be sufficient for ReplicationDomains
+ * that don't need to perform additional computing.
+ *
+ * @param initStatus The status to enter the state machine with.
+ * @param replicationServerState The ServerState of the ReplicationServer
+ * with which the session was established.
+ * @param session The ProtocolSession that is currently used.
+ */
+ public void sessionInitiated(
+ ServerStatus initStatus,
+ ServerState replicationServerState,
+ ProtocolSession session)
+ {
+ // Sanity check: is it a valid initial status?
+ if (!isValidInitialStatus(initStatus))
+ {
+ Message msg = ERR_DS_INVALID_INIT_STATUS.get(initStatus.toString(),
+ serviceID, Short.toString(serverID));
+ logError(msg);
+ } else
+ {
+ status = initStatus;
+ }
+ }
+
+ /**
+ * Processes an incoming ChangeStatusMsg. Compute new status according to
+ * given order. Then update domain for being compliant with new status
+ * definition.
+ * @param csMsg The received status message
+ */
+ private void receiveChangeStatus(ChangeStatusMsg csMsg)
+ {
+ if (debugEnabled())
+ TRACER.debugInfo("Replication domain " + serviceID +
+ " received change status message:\n" + csMsg);
+
+ ServerStatus reqStatus = csMsg.getRequestedStatus();
+
+ // Translate requested status to a state machine event
+ StatusMachineEvent event = StatusMachineEvent.statusToEvent(reqStatus);
+ if (event == StatusMachineEvent.INVALID_EVENT)
+ {
+ Message msg = ERR_DS_INVALID_REQUESTED_STATUS.get(reqStatus.toString(),
+ serviceID, Short.toString(serverID));
+ logError(msg);
+ return;
+ }
+
+ // Set the new status to the requested one
+ setNewStatus(event);
+ }
+
+ /**
+ * Called when first connection or disconnection detected.
+ */
+ void toNotConnectedStatus()
+ {
+ // Go into not connected status
+ setNewStatus(StatusMachineEvent.TO_NOT_CONNECTED_STATUS_EVENT);
+ }
+
+ /**
+ * Perform whatever actions are needed to apply properties for being
+ * compliant with new status. Must be called in synchronized section for
+ * status. The new status is already set in status variable.
+ */
+ private void updateDomainForNewStatus()
+ {
+ switch (status)
+ {
+ case NOT_CONNECTED_STATUS:
+ break;
+ case NORMAL_STATUS:
+ break;
+ case DEGRADED_STATUS:
+ break;
+ case FULL_UPDATE_STATUS:
+ // Signal RS we just entered the full update status
+ broker.signalStatusChange(status);
+ break;
+ case BAD_GEN_ID_STATUS:
+ break;
+ default:
+ if (debugEnabled())
+ TRACER.debugInfo("updateDomainForNewStatus: unexpected status: " +
+ status);
+ }
+ }
+
+ /**
+ * Gets the status for this domain.
+ * @return The status for this domain.
+ */
+ public ServerStatus getStatus()
+ {
+ return status;
+ }
+
+ /**
+ * Gets the identifier of this domain.
+ *
+ * @return The identifier for this domain.
+ */
+ public String getServiceID()
+ {
+ return serviceID;
+ }
+
+ /**
+ * Get the server ID.
+ * @return The server ID.
+ */
+ public short getServerId()
+ {
+ return serverID;
+ }
+
+ /**
+ * Tells if assured replication is enabled for this domain.
+ * @return True if assured replication is enabled for this domain.
+ */
+ public boolean isAssured()
+ {
+ return assured;
+ }
+
+ /**
+ * Gives the mode for the assured replication of the domain.
+ * @return The mode for the assured replication of the domain.
+ */
+ public AssuredMode getAssuredMode()
+ {
+ return assuredMode;
+ }
+
+ /**
+ * Gives the assured level of the replication of the domain.
+ * @return The assured level of the replication of the domain.
+ */
+ public byte getAssuredSdLevel()
+ {
+ return assuredSdLevel;
+ }
+
+ /**
+ * Gives the assured timeout of the replication of the domain (in ms).
+ * @return The assured timeout of the replication of the domain.
+ */
+ public long getAssuredTimeout()
+ {
+ return assuredTimeout;
+ }
+
+ /**
+ * Gets the group id for this domain.
+ * @return The group id for this domain.
+ */
+ public byte getGroupId()
+ {
+ return groupId;
+ }
+
+ /**
+ * Gets the referrals URLs this domain publishes.
+ * @return The referrals URLs this domain publishes.
+ */
+ public List<String> getRefUrls()
+ {
+ return refUrls;
+ }
+
+ /**
+ * Gets the info for DSs in the topology (except us).
+ * @return The info for DSs in the topology (except us)
+ */
+ public List<DSInfo> getDsList()
+ {
+ return broker.getDsList();
+ }
+
+ /**
+ * Gets the info for RSs in the topology (except the one we are connected
+ * to).
+ * @return The info for RSs in the topology (except the one we are connected
+ * to)
+ */
+ public List<RSInfo> getRsList()
+ {
+ return broker.getRsList();
+ }
+
+
+ /**
+ * Gets the server ID of the Replication Server to which the domain
+ * is currently connected.
+ *
+ * @return The server ID of the Replication Server to which the domain
+ * is currently connected.
+ */
+ public short getRsServerId()
+ {
+ return broker.getRsServerId();
+ }
+
+ /**
+ * Increment the number of processed updates.
+ */
+ private void incProcessedUpdates()
+ {
+ numProcessedUpdates.incrementAndGet();
+ }
+
+ /**
+ * get the number of updates replayed by the replication.
+ *
+ * @return The number of updates replayed by the replication
+ */
+ int getNumProcessedUpdates()
+ {
+ if (numProcessedUpdates != null)
+ return numProcessedUpdates.get();
+ else
+ return 0;
+ }
+
+ /**
+ * get the number of updates received by the replication plugin.
+ *
+ * @return the number of updates received
+ */
+ int getNumRcvdUpdates()
+ {
+ if (numRcvdUpdates != null)
+ return numRcvdUpdates.get();
+ else
+ return 0;
+ }
+
+ /**
+ * Get the number of updates sent by the replication plugin.
+ *
+ * @return the number of updates sent
+ */
+ int getNumSentUpdates()
+ {
+ if (numSentUpdates != null)
+ return numSentUpdates.get();
+ else
+ return 0;
+ }
+
+ /**
+ * Set the list of Referrals that should be returned when an
+ * operation needs to be redirected to this server.
+ *
+ * @param referralsUrl The list of referrals.
+ */
+ public void setURLs(Set<String> referralsUrl)
+ {
+ for (String url : referralsUrl)
+ this.refUrls.add(url);
+ }
+
+ /**
+ * Set the timeout of the assured replication.
+ *
+ * @param assuredTimeout the timeout of the assured replication.
+ */
+ public void setAssuredTimeout(long assuredTimeout)
+ {
+ this.assuredTimeout = assuredTimeout;
+ }
+
+ /**
+ * Sets the groupID.
+ *
+ * @param groupId The groupID.
+ */
+ public void setGroupId(byte groupId)
+ {
+ this.groupId = groupId;
+ }
+
+ /**
+ * Sets the level of assured replication.
+ *
+ * @param assuredSdLevel The level of assured replication.
+ */
+ public void setAssuredSdLevel(byte assuredSdLevel)
+ {
+ this.assuredSdLevel = assuredSdLevel;
+ }
+
+ /**
+ * Sets the assured replication mode.
+ *
+ * @param dataMode The assured replication mode.
+ */
+ public void setAssuredMode(AssuredMode dataMode)
+ {
+ this.assuredMode = dataMode;
+ }
+
+ /**
+ * Sets assured replication.
+ *
+ * @param assured A boolean indicating if assured replication should be used.
+ */
+ public void setAssured(boolean assured)
+ {
+ this.assured = assured;
+ }
+
+ /**
+ * Receives an update message from the replicationServer.
+ * also responsible for updating the list of pending changes
+ * @return the received message - null if none
+ */
+ UpdateMsg receive()
+ {
+ UpdateMsg update = null;
+
+ while (update == null)
+ {
+ InitializeRequestMsg initMsg = null;
+ ReplicationMsg msg;
+ try
+ {
+ msg = broker.receive();
+ if (msg == null)
+ {
+ // The server is in the shutdown process
+ return null;
+ }
+
+ if (debugEnabled())
+ if (!(msg instanceof HeartbeatMsg))
+ TRACER.debugVerbose("Message received <" + msg + ">");
+
+ if (msg instanceof AckMsg)
+ {
+ AckMsg ack = (AckMsg) msg;
+ receiveAck(ack);
+ }
+ else if (msg instanceof InitializeRequestMsg)
+ {
+ // Another server requests us to provide entries
+ // for a total update
+ initMsg = (InitializeRequestMsg)msg;
+ }
+ else if (msg instanceof InitializeTargetMsg)
+ {
+ // Another server is exporting its entries to us
+ InitializeTargetMsg importMsg = (InitializeTargetMsg) msg;
+
+ try
+ {
+ // This must be done while we are still holding the
+ // broker lock because we are now going to receive a
+ // bunch of entries from the remote server and we
+ // want the import thread to catch them and
+ // not the ListenerThread.
+ initialize(importMsg);
+ }
+ catch(DirectoryException de)
+ {
+ // Returns an error message to notify the sender
+ ErrorMsg errorMsg =
+ new ErrorMsg(importMsg.getsenderID(),
+ de.getMessageObject());
+ MessageBuilder mb = new MessageBuilder();
+ mb.append(de.getMessageObject());
+ TRACER.debugInfo(Message.toString(mb.toMessage()));
+ broker.publish(errorMsg);
+ }
+ }
+ else if (msg instanceof ErrorMsg)
+ {
+ if (ieContext != null)
+ {
+ // This is an error termination for the 2 following cases :
+ // - either during an export
+ // - or before an import really started
+ // For example, when we publish a request and the
+ // replicationServer did not find any import source.
+ abandonImportExport((ErrorMsg)msg);
+ }
+ else
+ {
+ /*
+ * Log error message
+ */
+ ErrorMsg errorMsg = (ErrorMsg)msg;
+ logError(ERR_ERROR_MSG_RECEIVED.get(
+ errorMsg.getDetails()));
+ }
+ }
+ else if (msg instanceof ChangeStatusMsg)
+ {
+ ChangeStatusMsg csMsg = (ChangeStatusMsg)msg;
+ receiveChangeStatus(csMsg);
+ }
+ else if (msg instanceof UpdateMsg)
+ {
+ generator.adjust(((UpdateMsg) msg).getChangeNumber());
+ update = (UpdateMsg) msg;
+ generator.adjust(update.getChangeNumber());
+ }
+ }
+ catch (SocketTimeoutException e)
+ {
+ // just retry
+ }
+ // Test if we have received and export request message and
+ // if that's the case handle it now.
+ // This must be done outside of the portion of code protected
+ // by the broker lock so that we keep receiveing update
+ // when we are doing and export and so that a possible
+ // closure of the socket happening when we are publishing the
+ // entries to the remote can be handled by the other
+ // replay thread when they call this method and therefore the
+ // broker.receive() method.
+ if (initMsg != null)
+ {
+ // Do this work in a thread to allow replay thread continue working
+ ExportThread exportThread = new ExportThread(initMsg.getsenderID());
+ exportThread.start();
+ }
+ }
+
+ numRcvdUpdates.incrementAndGet();
+ return update;
+ }
+
+ /**
+ * Wait for the processing of an assured message.
+ *
+ * @param msg The UpdateMsg for which we are waiting for an ack.
+ * @throws TimeoutException When the configured timeout occurs waiting for the
+ * ack.
+ */
+ private void waitForAck(UpdateMsg msg) throws TimeoutException
+ {
+ // Wait for the ack to be received, timing out if necessary
+ long startTime = System.currentTimeMillis();
+ synchronized (msg)
+ {
+ ChangeNumber cn = msg.getChangeNumber();
+ while (waitingAckMsgs.containsKey(cn))
+ {
+ try
+ {
+ // WARNING: this timeout may be difficult to optimize: too low, it
+ // may use too much CPU, too high, it may penalize performance...
+ msg.wait(10);
+ } catch (InterruptedException e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugInfo("waitForAck method interrupted for replication " +
+ "serviceID: " + serviceID);
+ }
+ break;
+ }
+ // Timeout ?
+ if ( (System.currentTimeMillis() - startTime) >= assuredTimeout )
+ {
+ // Timeout occured, be sure that ack is not being received and if so,
+ // remove the update from the wait list, log the timeout error and
+ // also update assured monitoring counters
+ UpdateMsg update;
+ synchronized (waitingAckMsgs)
+ {
+ update = waitingAckMsgs.remove(cn);
+ }
+
+ if (update != null)
+ {
+ // No luck, this is a real timeout
+ // Increment assured replication monitoring counters
+ switch (msg.getAssuredMode())
+ {
+ case SAFE_READ_MODE:
+ assuredSrNotAcknowledgedUpdates.incrementAndGet();
+ assuredSrTimeoutUpdates.incrementAndGet();
+ // Increment number of errors for our RS
+ updateAssuredErrorsByServer(
+ assuredSrServerNotAcknowledgedUpdates,
+ broker.getRsServerId());
+ break;
+ case SAFE_DATA_MODE:
+ assuredSdTimeoutUpdates.incrementAndGet();
+ // Increment number of errors for our RS
+ updateAssuredErrorsByServer(assuredSdServerTimeoutUpdates,
+ broker.getRsServerId());
+ break;
+ default:
+ // Should no happen
+ }
+
+ throw new TimeoutException("No ack received for message cn: " + cn +
+ " and replication servceID: " + serviceID + " after " +
+ assuredTimeout + " ms.");
+ } else
+ {
+ // Ack received just before timeout limit: we can exit
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates the passed monitoring list of errors received for assured messages
+ * (safe data or safe read, depending of the passed list to update) for a
+ * particular server in the list. This increments the counter of error for the
+ * passed server, or creates an initial value of 1 error for it if the server
+ * is not yet present in the map.
+ * @param errorList
+ * @param serverId
+ */
+ private void updateAssuredErrorsByServer(Map<Short,Integer> errorsByServer,
+ Short serverId)
+ {
+ synchronized (errorsByServer)
+ {
+ Integer serverErrCount = errorsByServer.get(serverId);
+ if (serverErrCount == null)
+ {
+ // Server not present in list, create an entry with an
+ // initial number of errors set to 1
+ errorsByServer.put(serverId, 1);
+ } else
+ {
+ // Server already present in list, just increment number of
+ // errors for the server
+ int val = serverErrCount.intValue();
+ val++;
+ errorsByServer.put(serverId, val);
+ }
+ }
+ }
+
+ /**
+ * Do the necessary processing when an AckMsg is received.
+ *
+ * @param ack The AckMsg that was received.
+ */
+ private void receiveAck(AckMsg ack)
+ {
+ UpdateMsg update;
+ ChangeNumber changeNumber = ack.getChangeNumber();
+
+ // Remove the message for pending ack list (this may already make the thread
+ // that is waiting for the ack be aware of its reception)
+ synchronized (waitingAckMsgs)
+ {
+ update = waitingAckMsgs.remove(changeNumber);
+ }
+
+ // Signal waiting thread ack has been received
+ if (update != null)
+ {
+ synchronized (update)
+ {
+ update.notify();
+ }
+
+ // Analyze status of embedded in the ack to see if everything went well
+ boolean hasTimeout = ack.hasTimeout();
+ boolean hasReplayErrors = ack.hasReplayError();
+ boolean hasWrongStatus = ack.hasWrongStatus();
+
+ AssuredMode updateAssuredMode = update.getAssuredMode();
+
+ if ( hasTimeout || hasReplayErrors || hasWrongStatus)
+ {
+ // Some problems detected: message not correclty reached every requested
+ // servers. Log problem
+ Message errorMsg = ERR_DS_RECEIVED_ACK_ERROR.get(serviceID,
+ update.toString(), ack.errorsToString());
+ logError(errorMsg);
+
+ List<Short> failedServers = ack.getFailedServers();
+
+ // Increment assured replication monitoring counters
+ switch (updateAssuredMode)
+ {
+ case SAFE_READ_MODE:
+ assuredSrNotAcknowledgedUpdates.incrementAndGet();
+ if (hasTimeout)
+ assuredSrTimeoutUpdates.incrementAndGet();
+ if (hasReplayErrors)
+ assuredSrReplayErrorUpdates.incrementAndGet();
+ if (hasWrongStatus)
+ assuredSrWrongStatusUpdates.incrementAndGet();
+ if (failedServers != null) // This should always be the case !
+ {
+ for(Short sid : failedServers)
+ {
+ updateAssuredErrorsByServer(
+ assuredSrServerNotAcknowledgedUpdates, sid);
+ }
+ }
+ break;
+ case SAFE_DATA_MODE:
+ // The only possible cause of ack error in safe data mode is timeout
+ if (hasTimeout) // So should always be the case
+ assuredSdTimeoutUpdates.incrementAndGet();
+ if (failedServers != null) // This should always be the case !
+ {
+ for(Short sid : failedServers)
+ {
+ updateAssuredErrorsByServer(
+ assuredSdServerTimeoutUpdates, sid);
+ }
+ }
+ break;
+ default:
+ // Should no happen
+ }
+ } else
+ {
+ // Update has been acknowledged without errors
+ // Increment assured replication monitoring counters
+ switch (updateAssuredMode)
+ {
+ case SAFE_READ_MODE:
+ assuredSrAcknowledgedUpdates.incrementAndGet();
+ break;
+ case SAFE_DATA_MODE:
+ assuredSdAcknowledgedUpdates.incrementAndGet();
+ break;
+ default:
+ // Should no happen
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieves a replication domain based on the baseDn.
+ *
+ * @param serviceID The identifier of the domain to retrieve.
+ *
+ * @return The domain retrieved.
+ *
+ * @throws DirectoryException When an error occurred or no domain
+ * match the provided baseDn.
+ */
+ static ReplicationDomain retrievesReplicationDomain(String serviceID)
+ throws DirectoryException
+ {
+ ReplicationDomain replicationDomain = domains.get(serviceID);
+ if (replicationDomain == null)
+ {
+ MessageBuilder mb = new MessageBuilder(ERR_NO_MATCHING_DOMAIN.get());
+ mb.append(" ");
+ mb.append(serviceID);
+ throw new DirectoryException(ResultCode.OTHER,
+ mb.toMessage());
+ }
+ return replicationDomain;
+ }
+
+ /*
+ * After this point the code is related to the Total Update.
+ */
+
+ /**
+ * This thread is launched when we want to export data to another server that
+ * has requested to be initialized with the data of our backend.
+ */
+ private class ExportThread extends DirectoryThread
+ {
+ // Id of server that will receive updates
+ private short target;
+
+ /**
+ * Constructor for the ExportThread.
+ *
+ * @param target Id of server that will receive updates
+ */
+ public ExportThread(short target)
+ {
+ super("Export thread " + serverID);
+ this.target = target;
+ }
+
+ /**
+ * Run method for this class.
+ */
+ public void run()
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugInfo("Export thread starting.");
+ }
+
+ try
+ {
+ initializeRemote(target, target, null);
+ } catch (DirectoryException de)
+ {
+ // An error message has been sent to the peer
+ // Nothing more to do locally
+ }
+ if (debugEnabled())
+ {
+ TRACER.debugInfo("Export thread stopping.");
+ }
+ }
+ }
+
+ /**
+ * This class contain the context related to an import or export
+ * launched on the domain.
+ */
+ private class IEContext
+ {
+ // The task that initiated the operation.
+ Task initializeTask;
+ // The input stream for the import
+ ReplInputStream ldifImportInputStream = null;
+ // The target in the case of an export
+ short exportTarget = RoutableMsg.UNKNOWN_SERVER;
+ // The source in the case of an import
+ short importSource = RoutableMsg.UNKNOWN_SERVER;
+
+ // The total entry count expected to be processed
+ long entryCount = 0;
+ // The count for the entry not yet processed
+ long entryLeftCount = 0;
+
+ // The exception raised when any
+ DirectoryException exception = null;
+
+ // A boolean indicating if the context is related to an
+ // import or an export.
+ boolean importInProgress;
+
+ /**
+ * Creates a new IEContext.
+ *
+ * @param importInProgress true if the IEContext will be used
+ * for and import, false if the IEContext
+ * will be used for and export.
+ */
+ public IEContext(boolean importInProgress)
+ {
+ this.importInProgress = importInProgress;
+ }
+
+ /**
+ * Initializes the import/export counters with the provider value.
+ * @param total
+ * @param left
+ * @throws DirectoryException
+ */
+ public void setCounters(long total, long left)
+ throws DirectoryException
+ {
+ entryCount = total;
+ entryLeftCount = left;
+
+ if (initializeTask != null)
+ {
+ if (initializeTask instanceof InitializeTask)
+ {
+ ((InitializeTask)initializeTask).setTotal(entryCount);
+ ((InitializeTask)initializeTask).setLeft(entryCount);
+ }
+ else if (initializeTask instanceof InitializeTargetTask)
+ {
+ ((InitializeTargetTask)initializeTask).setTotal(entryCount);
+ ((InitializeTargetTask)initializeTask).setLeft(entryCount);
+ }
+ }
+ }
+
+ /**
+ * Update the counters of the task for each entry processed during
+ * an import or export.
+ * @throws DirectoryException
+ */
+ public void updateCounters()
+ throws DirectoryException
+ {
+ entryLeftCount--;
+
+ if (initializeTask != null)
+ {
+ if (initializeTask instanceof InitializeTask)
+ {
+ ((InitializeTask)initializeTask).setLeft(entryLeftCount);
+ }
+ else if (initializeTask instanceof InitializeTargetTask)
+ {
+ ((InitializeTargetTask)initializeTask).setLeft(entryLeftCount);
+ }
+ }
+ }
+
+ /**
+ * Update the counters of the task for each entry processed during
+ * an import or export.
+ *
+ * @param entriesDone The number of entries that were processed
+ * since the last time this method was called.
+ *
+ * @throws DirectoryException
+ */
+ public void updateCounters(int entriesDone)
+ throws DirectoryException
+ {
+ entryLeftCount -= entriesDone;
+
+ if (initializeTask != null)
+ {
+ if (initializeTask instanceof InitializeTask)
+ {
+ ((InitializeTask)initializeTask).setLeft(entryLeftCount);
+ }
+ else if (initializeTask instanceof InitializeTargetTask)
+ {
+ ((InitializeTargetTask)initializeTask).setLeft(entryLeftCount);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString()
+ {
+ return new String("[ Entry count=" + this.entryCount +
+ ", Entry left count=" + this.entryLeftCount + "]");
+ }
+ }
+ /**
+ * Verifies that the given string represents a valid source
+ * from which this server can be initialized.
+ *
+ * @param targetString The string representing the source
+ * @return The source as a short value
+ * @throws DirectoryException if the string is not valid
+ */
+ short decodeTarget(String targetString)
+ throws DirectoryException
+ {
+ short target = 0;
+ Throwable cause;
+ if (targetString.equalsIgnoreCase("all"))
+ {
+ return RoutableMsg.ALL_SERVERS;
+ }
+
+ // So should be a serverID
+ try
+ {
+ target = Integer.decode(targetString).shortValue();
+ if (target >= 0)
+ {
+ // FIXME Could we check now that it is a know server in the domain ?
+ }
+ return target;
+ }
+ catch(Exception e)
+ {
+ cause = e;
+ }
+ ResultCode resultCode = ResultCode.OTHER;
+ Message message = ERR_INVALID_EXPORT_TARGET.get();
+
+ if (cause != null)
+ throw new DirectoryException(
+ resultCode, message, cause);
+ else
+ throw new DirectoryException(
+ resultCode, message);
+
+ }
+
+ /**
+ * Process the initialization of some other server or servers in the topology
+ * specified by the target argument.
+ * @param target The target that should be initialized
+ * @param initTask The task that triggers this initialization and that should
+ * be updated with its progress.
+ *
+ * @exception DirectoryException When an error occurs.
+ */
+ void initializeRemote(short target, Task initTask)
+ throws DirectoryException
+ {
+ initializeRemote(target, serverID, initTask);
+ }
+
+ /**
+ * Process the initialization of some other server or servers in the topology
+ * specified by the target argument when this initialization specifying the
+ * server that requests the initialization.
+ *
+ * @param target The target that should be initialized.
+ * @param requestorID The server that initiated the export.
+ * @param initTask The task that triggers this initialization and that should
+ * be updated with its progress.
+ *
+ * @exception DirectoryException When an error occurs.
+ */
+ void initializeRemote(short target, short requestorID, Task initTask)
+ throws DirectoryException
+ {
+ Message msg = NOTE_FULL_UPDATE_ENGAGED_FOR_REMOTE_START.get(
+ Short.toString(serverID),
+ serviceID,
+ Short.toString(requestorID));
+ logError(msg);
+
+ boolean contextAcquired=false;
+
+ acquireIEContext(false);
+ contextAcquired = true;
+ ieContext.exportTarget = target;
+
+ if (initTask != null)
+ {
+ ieContext.initializeTask = initTask;
+ }
+
+ // The number of entries to be exported is the number of entries under
+ // the base DN entry and the base entry itself.
+ long entryCount = this.countEntries();
+
+
+ ieContext.setCounters(entryCount, entryCount);
+
+ // Send start message to the peer
+ InitializeTargetMsg initializeMessage = new InitializeTargetMsg(
+ serviceID, serverID, target, requestorID, entryCount);
+
+ broker.publish(initializeMessage);
+
+ try
+ {
+ exportBackend(new BufferedOutputStream(new ReplOutputStream(this)));
+
+ // Notify the peer of the success
+ DoneMsg doneMsg = new DoneMsg(serverID,
+ initializeMessage.getDestination());
+ broker.publish(doneMsg);
+
+ releaseIEContext();
+ }
+ catch(DirectoryException de)
+ {
+ // Notify the peer of the failure
+ ErrorMsg errorMsg =
+ new ErrorMsg(target,
+ de.getMessageObject());
+ broker.publish(errorMsg);
+
+ if (contextAcquired)
+ releaseIEContext();
+
+ throw(de);
+ }
+
+ msg = NOTE_FULL_UPDATE_ENGAGED_FOR_REMOTE_END.get(
+ Short.toString(serverID),
+ serviceID,
+ Short.toString(requestorID));
+ logError(msg);
+ }
+
+ /**
+ * Get the ServerState maintained by the Concrete class.
+ *
+ * @return the ServerState maintained by the Concrete class.
+ */
+ public ServerState getServerState()
+ {
+ return state;
+ }
+
+
+ private synchronized void acquireIEContext(boolean importInProgress)
+ throws DirectoryException
+ {
+ if (ieContext != null)
+ {
+ // Rejects 2 simultaneous exports
+ Message message = ERR_SIMULTANEOUS_IMPORT_EXPORT_REJECTED.get();
+ throw new DirectoryException(ResultCode.OTHER,
+ message);
+ }
+
+ ieContext = new IEContext(importInProgress);
+ }
+
+ private synchronized void releaseIEContext()
+ {
+ ieContext = null;
+ }
+
+ /**
+ * Processes an error message received while an import/export is
+ * on going.
+ * @param errorMsg The error message received.
+ */
+ void abandonImportExport(ErrorMsg errorMsg)
+ {
+ // FIXME TBD Treat the case where the error happens while entries
+ // are being exported
+
+ if (debugEnabled())
+ TRACER.debugVerbose(
+ " abandonImportExport:" + this.serverID +
+ " serviceID: " + this.serviceID +
+ " Error Msg received: " + errorMsg);
+
+ if (ieContext != null)
+ {
+ ieContext.exception = new DirectoryException(ResultCode.OTHER,
+ errorMsg.getDetails());
+
+ if (ieContext.initializeTask instanceof InitializeTask)
+ {
+ // Update the task that initiated the import
+ ((InitializeTask)ieContext.initializeTask).
+ updateTaskCompletionState(ieContext.exception);
+
+ releaseIEContext();
+ }
+ }
+ }
+
+ /**
+ * Receives bytes related to an entry in the context of an import to
+ * initialize the domain (called by ReplLDIFInputStream).
+ *
+ * @return The bytes. Null when the Done or Err message has been received
+ */
+ byte[] receiveEntryBytes()
+ {
+ ReplicationMsg msg;
+ while (true)
+ {
+ try
+ {
+ msg = broker.receive();
+
+ if (debugEnabled())
+ TRACER.debugVerbose(
+ " sid:" + serverID +
+ " base DN:" + serviceID +
+ " Import EntryBytes received " + msg);
+ if (msg == null)
+ {
+ // The server is in the shutdown process
+ return null;
+ }
+
+ if (msg instanceof EntryMsg)
+ {
+ EntryMsg entryMsg = (EntryMsg)msg;
+ byte[] entryBytes = entryMsg.getEntryBytes();
+ ieContext.updateCounters(countEntryLimits(entryBytes));
+ return entryBytes;
+ }
+ else if (msg instanceof DoneMsg)
+ {
+ // This is the normal termination of the import
+ // No error is stored and the import is ended
+ // by returning null
+ return null;
+ }
+ else if (msg instanceof ErrorMsg)
+ {
+ // This is an error termination during the import
+ // The error is stored and the import is ended
+ // by returning null
+ ErrorMsg errorMsg = (ErrorMsg)msg;
+ ieContext.exception = new DirectoryException(
+ ResultCode.OTHER,
+ errorMsg.getDetails());
+ return null;
+ }
+ else
+ {
+ // Other messages received during an import are trashed
+ }
+ }
+ catch(Exception e)
+ {
+ // TODO: i18n
+ ieContext.exception = new DirectoryException(ResultCode.OTHER,
+ Message.raw("received an unexpected message type" +
+ e.getLocalizedMessage()));
+ }
+ }
+ }
+
+ /**
+ * Count the number of entries in the provided byte[].
+ * This is based on the hypothesis that the entries are separated
+ * by a "\n\n" String.
+ *
+ * @param entryBytes
+ * @return The number of entries in the provided byte[].
+ */
+ private int countEntryLimits(byte[] entryBytes)
+ {
+ return countEntryLimits(entryBytes, 0, entryBytes.length);
+ }
+
+ /**
+ * Count the number of entries in the provided byte[].
+ * This is based on the hypothesis that the entries are separated
+ * by a "\n\n" String.
+ *
+ * @param entryBytes
+ * @return The number of entries in the provided byte[].
+ */
+ private int countEntryLimits(byte[] entryBytes, int pos, int length)
+ {
+ int entryCount = 0;
+ int count = 0;
+ while (count<=length-2)
+ {
+ if ((entryBytes[pos+count] == '\n') && (entryBytes[pos+count+1] == '\n'))
+ {
+ entryCount++;
+ count++;
+ }
+ count++;
+ }
+ return entryCount;
+ }
+
+ /**
+ * Exports an entry in LDIF format.
+ *
+ * @param lDIFEntry The entry to be exported in byte[] form.
+ * @param pos The starting Position in the array.
+ * @param length Number of array elements to be copied.
+ *
+ * @throws IOException when an error occurred.
+ */
+ void exportLDIFEntry(byte[] lDIFEntry, int pos, int length) throws IOException
+ {
+ // If an error was raised - like receiving an ErrorMsg
+ // we just let down the export.
+ if (ieContext.exception != null)
+ {
+ IOException ioe = new IOException(ieContext.exception.getMessage());
+ ieContext = null;
+ throw ioe;
+ }
+
+ EntryMsg entryMessage = new EntryMsg(
+ serverID, ieContext.exportTarget, lDIFEntry, pos, length);
+ broker.publish(entryMessage);
+
+ try
+ {
+ ieContext.updateCounters(countEntryLimits(lDIFEntry, pos, length));
+ }
+ catch (DirectoryException de)
+ {
+ throw new IOException(de.getMessage());
+ }
+ }
+
+ /**
+ * Initializes this domain from another source server.
+ *
+ * @param source The source from which to initialize
+ * @param initTask The task that launched the initialization
+ * and should be updated of its progress.
+ * @throws DirectoryException when an error occurs
+ */
+ void initializeFromRemote(short source, Task initTask)
+ throws DirectoryException
+ {
+ if (debugEnabled())
+ TRACER.debugInfo("Entering initializeFromRemote");
+
+ acquireIEContext(true);
+ ieContext.initializeTask = initTask;
+
+ InitializeRequestMsg initializeMsg = new InitializeRequestMsg(
+ serviceID, serverID, source);
+
+ // Publish Init request msg
+ broker.publish(initializeMsg);
+
+ // .. we expect to receive entries or err after that
+ }
+
+ /**
+ * Initializes the domain's backend with received entries.
+ * @param initializeMessage The message that initiated the import.
+ * @exception DirectoryException Thrown when an error occurs.
+ */
+ void initialize(InitializeTargetMsg initializeMessage)
+ throws DirectoryException
+ {
+ DirectoryException de = null;
+
+ Message msg = NOTE_FULL_UPDATE_ENGAGED_FROM_REMOTE_START.get(
+ Short.toString(serverID),
+ serviceID,
+ Long.toString(initializeMessage.getRequestorID()));
+ logError(msg);
+
+ // Go into full update status
+ setNewStatus(StatusMachineEvent.TO_FULL_UPDATE_STATUS_EVENT);
+
+ if (initializeMessage.getRequestorID() == serverID)
+ {
+ // The import responds to a request we did so the IEContext
+ // is already acquired
+ }
+ else
+ {
+ acquireIEContext(true);
+ }
+
+ ieContext.importSource = initializeMessage.getsenderID();
+ ieContext.entryLeftCount = initializeMessage.getEntryCount();
+ ieContext.setCounters(
+ initializeMessage.getEntryCount(),
+ initializeMessage.getEntryCount());
+ ieContext.ldifImportInputStream = new ReplInputStream(this);
+
+ try
+ {
+ importBackend(new ReplInputStream(this));
+ broker.reStart();
+ }
+ catch (DirectoryException e)
+ {
+ de = e;
+ }
+ finally
+ {
+ if ((ieContext != null) && (ieContext.exception != null))
+ de = ieContext.exception;
+
+ // Update the task that initiated the import
+ if ((ieContext != null ) && (ieContext.initializeTask != null))
+ {
+ ((InitializeTask)ieContext.initializeTask).
+ updateTaskCompletionState(de);
+ }
+ releaseIEContext();
+ }
+
+ // Sends up the root error.
+ if (de != null)
+ {
+ throw de;
+ }
+
+ msg = NOTE_FULL_UPDATE_ENGAGED_FROM_REMOTE_END.get(
+ Short.toString(serverID),
+ serviceID,
+ Long.toString(initializeMessage.getRequestorID()));
+ logError(msg);
+ }
+
+ /**
+ * Sets the status to a new value depending of the passed status machine
+ * event.
+ * @param event The event that may make the status be changed
+ */
+ private void setNewStatus(StatusMachineEvent event)
+ {
+ ServerStatus newStatus =
+ StatusMachine.computeNewStatus(status, event);
+
+ if (newStatus == ServerStatus.INVALID_STATUS)
+ {
+ Message msg = ERR_DS_CANNOT_CHANGE_STATUS.get(serviceID,
+ Short.toString(serverID), status.toString(), event.toString());
+ logError(msg);
+ return;
+ }
+
+ if (newStatus != status)
+ {
+ // Store new status
+ status = newStatus;
+ lastStatusChangeDate = new Date();
+ // Reset assured monitoring counters (they are valid for a session with
+ // the same status/RS)
+ resetAssuredMonitoringCounters();
+
+ if (debugEnabled())
+ TRACER.debugInfo("Replication domain " + serviceID +
+ " new status is: " + status);
+
+ // Perform whatever actions are needed to apply properties for being
+ // compliant with new status
+ updateDomainForNewStatus();
+ }
+ }
+
+ /**
+ * Returns a boolean indicating if an import or export is currently
+ * processed.
+ *
+ * @return The status
+ */
+ public boolean ieRunning()
+ {
+ return (ieContext != null);
+ }
+
+ /**
+ * Verifies that the given string represents a valid source
+ * from which this server can be initialized.
+ * @param sourceString The string representing the source
+ * @return The source as a short value
+ * @throws DirectoryException if the string is not valid
+ */
+ short decodeSource(String sourceString)
+ throws DirectoryException
+ {
+ short source = 0;
+ Throwable cause = null;
+ try
+ {
+ source = Integer.decode(sourceString).shortValue();
+ if ((source >= -1) && (source != serverID))
+ {
+ // TODO Verifies serverID is in the domain
+ // We should check here that this is a server implied
+ // in the current domain.
+ return source;
+ }
+ }
+ catch(Exception e)
+ {
+ cause = e;
+ }
+
+ ResultCode resultCode = ResultCode.OTHER;
+ Message message = ERR_INVALID_IMPORT_SOURCE.get();
+ if (cause != null)
+ {
+ throw new DirectoryException(
+ resultCode, message, cause);
+ }
+ else
+ {
+ throw new DirectoryException(
+ resultCode, message);
+ }
+ }
+
+ /**
+ * Reset the generationId of this domain in the whole topology.
+ * A message is sent to the Replication Servers for them to reset
+ * their change dbs.
+ *
+ * @param generationIdNewValue The new value of the generation Id.
+ * @throws DirectoryException when an error occurs
+ */
+ void resetGenerationId(Long generationIdNewValue)
+ throws DirectoryException
+ {
+ if (debugEnabled())
+ TRACER.debugInfo(
+ "Server id " + serverID + " and domain " + serviceID
+ + "resetGenerationId" + generationIdNewValue);
+
+ if (!isConnected())
+ {
+ ResultCode resultCode = ResultCode.OTHER;
+ Message message = ERR_RESET_GENERATION_CONN_ERR_ID.get(
+ serviceID);
+ throw new DirectoryException(
+ resultCode, message);
+ }
+
+ ResetGenerationIdMsg genIdMessage = null;
+
+ if (generationIdNewValue == null)
+ {
+ genIdMessage = new ResetGenerationIdMsg(this.getGenerationID());
+ }
+ else
+ {
+ genIdMessage = new ResetGenerationIdMsg(generationIdNewValue);
+ }
+ broker.publish(genIdMessage);
+ }
+
+
+ /*
+ ******** End of The total Update code *********
+ */
+
+ /*
+ ******* Start of Monitoring Code **********
+ */
+
+ /**
+ * Get the maximum receive window size.
+ *
+ * @return The maximum receive window size.
+ */
+ int getMaxRcvWindow()
+ {
+ if (broker != null)
+ return broker.getMaxRcvWindow();
+ else
+ return 0;
+ }
+
+ /**
+ * Get the current receive window size.
+ *
+ * @return The current receive window size.
+ */
+ int getCurrentRcvWindow()
+ {
+ if (broker != null)
+ return broker.getCurrentRcvWindow();
+ else
+ return 0;
+ }
+
+ /**
+ * Get the maximum send window size.
+ *
+ * @return The maximum send window size.
+ */
+ int getMaxSendWindow()
+ {
+ if (broker != null)
+ return broker.getMaxSendWindow();
+ else
+ return 0;
+ }
+
+ /**
+ * Get the current send window size.
+ *
+ * @return The current send window size.
+ */
+ int getCurrentSendWindow()
+ {
+ if (broker != null)
+ return broker.getCurrentSendWindow();
+ else
+ return 0;
+ }
+
+ /**
+ * Get the number of times the replication connection was lost.
+ * @return The number of times the replication connection was lost.
+ */
+ int getNumLostConnections()
+ {
+ if (broker != null)
+ return broker.getNumLostConnections();
+ else
+ return 0;
+ }
+
+ /**
+ * Determine whether the connection to the replication server is encrypted.
+ * @return true if the connection is encrypted, false otherwise.
+ */
+ boolean isSessionEncrypted()
+ {
+ if (broker != null)
+ return broker.isSessionEncrypted();
+ else
+ return false;
+ }
+
+ /**
+ * This method is called when the ReplicationDomain has completed the
+ * processing of a received update synchronously.
+ * In such cases the processUpdateDone () is called and the state
+ * is updated automatically.
+ *
+ * @param msg The UpdateMessage that was processed.
+ */
+ void processUpdateDoneSynchronous(UpdateMsg msg)
+ {
+ // Warning: in synchronous mode, no way to tell the replay of an update went
+ // wrong Just put null in processUpdateDone so that if assured replication
+ // is used the ack is sent without error at replay flag.
+ processUpdateDone(msg, null);
+ state.update(msg.getChangeNumber());
+ }
+
+ /**
+ * Check if the domain is connected to a ReplicationServer.
+ *
+ * @return true if the server is connected, false if not.
+ */
+ public boolean isConnected()
+ {
+ if (broker != null)
+ return broker.isConnected();
+ else
+ return false;
+ }
+
+ /**
+ * Get the name of the replicationServer to which this domain is currently
+ * connected.
+ *
+ * @return the name of the replicationServer to which this domain
+ * is currently connected.
+ */
+ public String getReplicationServer()
+ {
+ if (broker != null)
+ return broker.getReplicationServer();
+ else
+ return "Not connected";
+ }
+
+ /**
+ * Gets the number of updates sent in assured safe read mode.
+ * @return The number of updates sent in assured safe read mode.
+ */
+ public int getAssuredSrSentUpdates()
+ {
+ return assuredSrSentUpdates.get();
+ }
+
+ /**
+ * Gets the number of updates sent in assured safe read mode that have been
+ * acknowledged without errors.
+ * @return The number of updates sent in assured safe read mode that have been
+ * acknowledged without errors.
+ */
+ public int getAssuredSrAcknowledgedUpdates()
+ {
+ return assuredSrAcknowledgedUpdates.get();
+ }
+
+ /**
+ * Gets the number of updates sent in assured safe read mode that have not
+ * been acknowledged.
+ * @return The number of updates sent in assured safe read mode that have not
+ * been acknowledged.
+ */
+ public int getAssuredSrNotAcknowledgedUpdates()
+ {
+ return assuredSrNotAcknowledgedUpdates.get();
+ }
+
+ /**
+ * Gets the number of updates sent in assured safe read mode that have not
+ * been acknowledged due to timeout error.
+ * @return The number of updates sent in assured safe read mode that have not
+ * been acknowledged due to timeout error.
+ */
+ public int getAssuredSrTimeoutUpdates()
+ {
+ return assuredSrTimeoutUpdates.get();
+ }
+
+ /**
+ * Gets the number of updates sent in assured safe read mode that have not
+ * been acknowledged due to wrong status error.
+ * @return The number of updates sent in assured safe read mode that have not
+ * been acknowledged due to wrong status error.
+ */
+ public int getAssuredSrWrongStatusUpdates()
+ {
+ return assuredSrWrongStatusUpdates.get();
+ }
+
+ /**
+ * Gets the number of updates sent in assured safe read mode that have not
+ * been acknowledged due to replay error.
+ * @return The number of updates sent in assured safe read mode that have not
+ * been acknowledged due to replay error.
+ */
+ public int getAssuredSrReplayErrorUpdates()
+ {
+ return assuredSrReplayErrorUpdates.get();
+ }
+
+ /**
+ * Gets the number of updates sent in assured safe read mode that have not
+ * been acknowledged per server.
+ * @return The number of updates sent in assured safe read mode that have not
+ * been acknowledged per server.
+ */
+ public Map<Short, Integer> getAssuredSrServerNotAcknowledgedUpdates()
+ {
+ return assuredSrServerNotAcknowledgedUpdates;
+ }
+
+ /**
+ * Gets the number of updates sent in assured safe data mode.
+ * @return The number of updates sent in assured safe data mode.
+ */
+ public int getAssuredSdSentUpdates()
+ {
+ return assuredSdSentUpdates.get();
+ }
+
+ /**
+ * Gets the number of updates sent in assured safe data mode that have been
+ * acknowledged without errors.
+ * @return The number of updates sent in assured safe data mode that have been
+ * acknowledged without errors.
+ */
+ public int getAssuredSdAcknowledgedUpdates()
+ {
+ return assuredSdAcknowledgedUpdates.get();
+ }
+
+ /**
+ * Gets the number of updates sent in assured safe data mode that have not
+ * been acknowledged due to timeout error.
+ * @return The number of updates sent in assured safe data mode that have not
+ * been acknowledged due to timeout error.
+ */
+ public int getAssuredSdTimeoutUpdates()
+ {
+ return assuredSdTimeoutUpdates.get();
+ }
+
+ /**
+ * Gets the number of updates sent in assured safe data mode that have not
+ * been acknowledged due to timeout error per server.
+ * @return The number of updates sent in assured safe data mode that have not
+ * been acknowledged due to timeout error per server.
+ */
+ public Map<Short, Integer> getAssuredSdServerTimeoutUpdates()
+ {
+ return assuredSdServerTimeoutUpdates;
+ }
+
+ /**
+ * Gets the date of the last status change.
+ * @return The date of the last status change.
+ */
+ public Date getLastStatusChangeDate()
+ {
+ return lastStatusChangeDate;
+ }
+
+ /**
+ * Resets the values of the monitoring counters for assured replication.
+ */
+ private void resetAssuredMonitoringCounters()
+ {
+ assuredSrSentUpdates = new AtomicInteger(0);
+ assuredSrAcknowledgedUpdates = new AtomicInteger(0);
+ assuredSrNotAcknowledgedUpdates = new AtomicInteger(0);
+ assuredSrTimeoutUpdates = new AtomicInteger(0);
+ assuredSrWrongStatusUpdates = new AtomicInteger(0);
+ assuredSrReplayErrorUpdates = new AtomicInteger(0);
+ assuredSrServerNotAcknowledgedUpdates = new HashMap<Short,Integer>();
+ assuredSdSentUpdates = new AtomicInteger(0);
+ assuredSdAcknowledgedUpdates = new AtomicInteger(0);
+ assuredSdTimeoutUpdates = new AtomicInteger(0);
+ assuredSdServerTimeoutUpdates = new HashMap<Short,Integer>();
+ }
+
+ /*
+ ********** End of Monitoring Code **************
+ */
+
+ /**
+ * Start the publish mechanism of the Replication Service.
+ * After this method has been called, the publish service can be used
+ * by calling the {@link #publish(UpdateMsg)} method.
+ *
+ * @param replicationServers The replication servers that should be used.
+ * @param window The window size of this replication domain.
+ * @param heartbeatInterval The heartbeatInterval that should be used
+ * to check the availability of the replication
+ * servers.
+ *
+ * @throws ConfigException If the DirectoryServer configuration was
+ * incorrect.
+ */
+ public void startPublishService(
+ Collection<String> replicationServers, int window,
+ long heartbeatInterval) throws ConfigException
+ {
+ /*
+ * create the broker object used to publish and receive changes
+ */
+ broker = new ReplicationBroker(
+ this, state, serviceID,
+ serverID, window,
+ getGenerationID(),
+ heartbeatInterval,
+ new ReplSessionSecurity(),
+ getGroupId());
+
+ broker.start(replicationServers);
+
+ /*
+ * Create a replication monitor object responsible for publishing
+ * monitoring information below cn=monitor.
+ */
+ monitor = new ReplicationMonitor(this);
+
+ DirectoryServer.registerMonitorProvider(monitor);
+ }
+
+ /**
+ * Starts the receiver side of the Replication Service.
+ * <p>
+ * After this method has been called, the Replication Service will start
+ * calling the {@link #processUpdate(UpdateMsg)}.
+ */
+ public void startListenService()
+ {
+ // Create the listener thread
+ listenerThread = new ListenerThread(this);
+ listenerThread.start();
+ }
+
+ /**
+ * Temporarily disable the Replication Service.
+ * The Replication Service can be enabled again using
+ * {@link #enableService()}.
+ * <p>
+ * It can be useful to disable the Replication Service when the
+ * repository where the replicated information is stored becomes
+ * temporarily unavailable and replicated updates can therefore not
+ * be replayed during a while.
+ */
+ public void disableService()
+ {
+ // Stop the listener thread
+ if (listenerThread != null)
+ {
+ listenerThread.shutdown();
+ }
+
+ if (broker != null)
+ {
+ broker.stop();
+ }
+
+ // Wait for the listener thread to stop
+ if (listenerThread != null)
+ listenerThread.waitForShutdown();
+ }
+
+ /**
+ * Restart the Replication service after a {@link #disableService()}.
+ * <p>
+ * The Replication Service will restart from the point indicated by the
+ * {@link ServerState} that was given as a parameter to the
+ * {@link #startPublishService(Collection, ServerState, int, long)}
+ * at startup time.
+ * If some data have changed in the repository during the period of time when
+ * the Replication Service was disabled, this {@link ServerState} should
+ * therefore be updated by the Replication Domain subclass before calling
+ * this method.
+ */
+ public void enableService()
+ {
+ broker.start();
+
+ // Create the listener thread
+ listenerThread = new ListenerThread(this);
+ listenerThread.start();
+ }
+
+ /**
+ * Definitively stops the Replication Service.
+ */
+ public void stopDomain()
+ {
+ DirectoryServer.deregisterMonitorProvider(monitor.getMonitorInstanceName());
+
+ disableService();
+ domains.remove(serviceID);
+ }
+
+ /**
+ * Change the ReplicationDomain parameters.
+ *
+ * @param replicationServers The new list of Replication Servers that this
+ * domain should now use.
+ * @param windowSize The window size that this domain should use.
+ * @param heartbeatInterval The heartbeatInterval that this domain should
+ * use.
+ */
+ public void changeConfig(
+ Collection<String> replicationServers,
+ int windowSize,
+ long heartbeatInterval)
+ {
+ if (broker != null)
+ {
+ if (broker.changeConfig(
+ replicationServers, windowSize, heartbeatInterval))
+ {
+ disableService();
+ enableService();
+ }
+ }
+ }
+
+
+ /**
+ * This method should trigger an export of the replicated data.
+ * to the provided outputStream.
+ * When finished the outputStream should be flushed and closed.
+ *
+ * @param output The OutputStream where the export should
+ * be produced.
+ * @throws DirectoryException When needed.
+ */
+ abstract protected void exportBackend(OutputStream output)
+ throws DirectoryException;
+
+ /**
+ * This method should trigger an import of the replicated data.
+ *
+ * @param input The InputStream from which
+ * the import should be reading entries.
+ *
+ * @throws DirectoryException When needed.
+ */
+ abstract protected void importBackend(InputStream input)
+ throws DirectoryException;
+
+ /**
+ * This method should return the total number of objects in the
+ * replicated domain.
+ * This count will be used for reporting.
+ *
+ * @throws DirectoryException when needed.
+ *
+ * @return The number of objects in the replication domain.
+ */
+ public abstract long countEntries() throws DirectoryException;
+
+ /**
+ * This method should handle the processing of {@link UpdateMsg} receive
+ * from remote replication entities.
+ * <p>
+ * This method will be called by a single thread and should therefore
+ * should not be blocking.
+ *
+ * @param updateMsg The {@link UpdateMsg} that was received.
+ *
+ * @return A boolean indicating if the processing is completed at return
+ * time.
+ * If <code> true </code> is returned, no further
+ * processing is necessary.
+ * If <code> false </code> is returned, the subclass should
+ * call the method
+ * {@link #processUpdateDone(UpdateMsg)}
+ * and update the ServerState
+ * When this processing is complete.
+ *
+ */
+ public abstract boolean processUpdate(UpdateMsg updateMsg);
+
+ /**
+ * This method must be called after each call to
+ * {@link #processUpdate(UpdateMsg)} when the processing of the update is
+ * completed.
+ * <p>
+ * It is useful for implementation needing to process the update in an
+ * asynchronous way or using several threads, but must be called even
+ * by implementation doing it in a synchronous, single-threaded way.
+ *
+ * @param msg The UpdateMsg whose processing was completed.
+ * @param replayErrorMsg if not null, this means an error occurred during the
+ * replay of this update, and this is the matching human readable message
+ * describing the problem.
+ */
+ public void processUpdateDone(UpdateMsg msg, String replayErrorMsg)
+ {
+ broker.updateWindowAfterReplay();
+
+ // Send an ack if it was requested and the group id is the same of the RS
+ // one. Only Safe Read mode makes sense in DS for returning an ack.
+ byte rsGroupId = broker.getRsGroupId();
+ if (msg.isAssured())
+ {
+ // Assured feature is supported starting from replication protocol V2
+ if (broker.getProtocolVersion() >=
+ ProtocolVersion.REPLICATION_PROTOCOL_V2)
+ {
+ AssuredMode msgAssuredMode = msg.getAssuredMode();
+ if (msgAssuredMode == AssuredMode.SAFE_READ_MODE)
+ {
+ if (rsGroupId == groupId)
+ {
+ // Send the ack
+ AckMsg ackMsg = new AckMsg(msg.getChangeNumber());
+ if (replayErrorMsg != null)
+ {
+ // Mark the error in the ack
+ // -> replay error occured
+ ackMsg.setHasReplayError(true);
+ // -> replay error occured in our server
+ List<Short> idList = new ArrayList<Short>();
+ idList.add(serverID);
+ ackMsg.setFailedServers(idList);
+ }
+ broker.publish(ackMsg);
+ }
+ } else if (assuredMode != AssuredMode.SAFE_DATA_MODE)
+ {
+ Message errorMsg = ERR_DS_UNKNOWN_ASSURED_MODE.get(
+ Short.toString(serverID), msgAssuredMode.toString(), serviceID,
+ msg.toString());
+ logError(errorMsg);
+ } else
+ {
+ // In safe data mode assured update that comes up to a DS requires no
+ // ack from a destinator DS. Safe data mode is based on RS acks only
+ }
+ }
+ }
+
+ incProcessedUpdates();
+ }
+
+ /**
+ * Publish an {@link UpdateMsg} to the Replication Service.
+ * <p>
+ * The Replication Service will handle the delivery of this {@link UpdateMsg}
+ * to all the participants of this Replication Domain.
+ * These members will be receive this {@link UpdateMsg} through a call
+ * of the {@link #processUpdate(UpdateMsg)} message.
+ *
+ * @param msg The UpdateMsg that should be pushed.
+ * @throws TimeoutException When assured replication is enabled and the
+ * configured timeout occurs when blocked waiting for the ack.
+ */
+ public void publish(UpdateMsg msg) throws TimeoutException
+ {
+ byte rsGroupId = broker.getRsGroupId();
+
+ // If assured configured, set message accordingly to request an ack in the
+ // right assured mode.
+ // No ack requested for a RS with a different group id. Assured replication
+ // suported for the same locality, i.e: a topology working in the same
+ // geographical location). If we are connected to a RS which is not in our
+ // locality, no need to ask for an ack.
+ if ( assured && ( rsGroupId == groupId ) )
+ {
+ msg.setAssured(true);
+ msg.setAssuredMode(assuredMode);
+ if (assuredMode == AssuredMode.SAFE_DATA_MODE)
+ msg.setSafeDataLevel(assuredSdLevel);
+
+ // Add the assured message to the list of update that are
+ // waiting for acks
+ synchronized (waitingAckMsgs)
+ {
+ waitingAckMsgs.put(msg.getChangeNumber(), msg);
+ }
+ }
+
+ // Publish the update
+ broker.publish(msg);
+ state.update(msg.getChangeNumber());
+ numSentUpdates.incrementAndGet();
+
+ // If assured mode configured, wait for acknowledgement for the just sent
+ // message
+ if ( assured && ( rsGroupId == groupId ) )
+ {
+ // Increment assured replication monitoring counters
+ switch(assuredMode)
+ {
+ case SAFE_READ_MODE:
+ assuredSrSentUpdates.incrementAndGet();
+ break;
+ case SAFE_DATA_MODE:
+ assuredSdSentUpdates.incrementAndGet();
+ break;
+ default:
+ // Should no happen
+ }
+
+ // Now wait for ack matching the sent assured update
+ waitForAck(msg);
+ }
+ }
+
+ /**
+ * Publish informations to the Replication Service (not assured mode).
+ *
+ * @param msg The byte array containing the informations that should
+ * be sent to the remote entities.
+ */
+ public void publish(byte[] msg)
+ {
+ UpdateMsg update = new UpdateMsg(generator.newChangeNumber(), msg);
+ try
+ {
+ publish(update);
+ } catch (TimeoutException e)
+ {
+ // Should never happen as assured mode not requested
+ }
+ }
+
+ /**
+ * This method should return the generationID to use for this
+ * ReplicationDomain.
+ * This method can be called at any time after the ReplicationDomain
+ * has been started.
+ *
+ * @return The GenerationID.
+ */
+ public abstract long getGenerationID();
+
+ /**
+ * Subclasses should use this method to add additional monitoring
+ * information in the ReplicationDomain.
+ *
+ * @return Additional monitoring attributes that will be added in the
+ * ReplicationDomain monitoring entry.
+ */
+ public Collection<Attribute> getAdditionalMonitoring()
+ {
+ return new ArrayList<Attribute>();
+ }
+
+ /**
+ * Returns a boolean indicating if a total update import is currently
+ * in Progress.
+ *
+ * @return A boolean indicating if a total update import is currently
+ * in Progress.
+ */
+ boolean importInProgress()
+ {
+ if (ieContext == null)
+ return false;
+ else
+ return ieContext.importInProgress;
+ }
+
+ /**
+ * Returns a boolean indicating if a total update export is currently
+ * in Progress.
+ *
+ * @return A boolean indicating if a total update export is currently
+ * in Progress.
+ */
+ boolean exportInProgress()
+ {
+ if (ieContext == null)
+ return false;
+ else
+ return !ieContext.importInProgress;
+ }
+
+ /**
+ * Returns the number of entries still to be processed when a total update
+ * is in progress.
+ *
+ * @return The number of entries still to be processed when a total update
+ * is in progress.
+ */
+ long getLeftEntryCount()
+ {
+ if (ieContext != null)
+ return ieContext.entryLeftCount;
+ else
+ return 0;
+ }
+
+ /**
+ * Returns the total number of entries to be processed when a total update
+ * is in progress.
+ *
+ * @return The total number of entries to be processed when a total update
+ * is in progress.
+ */
+ long getTotalEntryCount()
+ {
+ if (ieContext != null)
+ return ieContext.entryCount;
+ else
+ return 0;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationMonitor.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationMonitor.java
new file mode 100644
index 0000000..e677827
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/ReplicationMonitor.java
@@ -0,0 +1,331 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2006-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+import java.util.Collection;
+
+import java.util.ArrayList;
+
+import java.util.Map;
+import org.opends.server.admin.std.server.MonitorProviderCfg;
+import org.opends.server.api.MonitorProvider;
+import org.opends.server.core.DirectoryServer;
+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;
+
+/**
+ * Class used to generate monitoring information for the replication.
+ */
+public class ReplicationMonitor extends MonitorProvider<MonitorProviderCfg>
+{
+ private ReplicationDomain domain; // the replication plugin
+
+ /**
+ * Create a new replication monitor.
+ * @param domain the plugin which created the monitor
+ */
+ public ReplicationMonitor(ReplicationDomain domain)
+ {
+ super("Replication monitor " + domain.getServiceID()
+ + " " + domain.getServerId());
+ this.domain = domain;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializeMonitorProvider(MonitorProviderCfg configuration)
+ {
+ // no implementation needed.
+ }
+
+ /**
+ * Retrieves the name of this monitor provider. It should be unique among all
+ * monitor providers, including all instances of the same monitor provider.
+ *
+ * @return The name of this monitor provider.
+ */
+ @Override
+ public String getMonitorInstanceName()
+ {
+ return "Replication Domain " + domain.getServiceID()
+ + " " + domain.getServerId();
+ }
+
+ /**
+ * Retrieves a set of attributes containing monitor data that should be
+ * returned to the client if the corresponding monitor entry is requested.
+ *
+ * @return A set of attributes containing monitor data that should be
+ * returned to the client if the corresponding monitor entry is
+ * requested.
+ */
+ @Override
+ public ArrayList<Attribute> getMonitorData()
+ {
+ ArrayList<Attribute> attributes = new ArrayList<Attribute>();
+
+ /* get the base dn */
+ Attribute attr = Attributes.create("base-dn", domain.getServiceID());
+ attributes.add(attr);
+
+ /* get the base dn */
+ attr = Attributes.create("connected-to", domain
+ .getReplicationServer());
+ attributes.add(attr);
+
+ /* get number of lost connections */
+ addMonitorData(attributes, "lost-connections",
+ domain.getNumLostConnections());
+
+ /* get number of received updates */
+ addMonitorData(attributes, "received-updates", domain.getNumRcvdUpdates());
+
+ /* get number of updates sent */
+ addMonitorData(attributes, "sent-updates", domain.getNumSentUpdates());
+
+ /* get number of changes replayed */
+ addMonitorData(attributes, "replayed-updates",
+ domain.getNumProcessedUpdates());
+
+ /* get server-id */
+ addMonitorData(attributes, "server-id",
+ domain.getServerId());
+
+ /* get window information */
+ addMonitorData(attributes, "max-rcv-window", domain.getMaxRcvWindow());
+ addMonitorData(attributes, "current-rcv-window",
+ domain.getCurrentRcvWindow());
+ addMonitorData(attributes, "max-send-window",
+ domain.getMaxSendWindow());
+ addMonitorData(attributes, "current-send-window",
+ domain.getCurrentSendWindow());
+
+ /* get the Server State */
+ final String ATTR_SERVER_STATE = "server-state";
+ AttributeType type =
+ DirectoryServer.getDefaultAttributeType(ATTR_SERVER_STATE);
+ AttributeBuilder builder = new AttributeBuilder(type, ATTR_SERVER_STATE);
+ for (String str : domain.getServerState().toStringSet())
+ {
+ builder.add(new AttributeValue(type,str));
+ }
+ attributes.add(builder.toAttribute());
+
+ attributes.add(Attributes.create("ssl-encryption",
+ String.valueOf(domain.isSessionEncrypted())));
+
+ attributes.add(Attributes.create("generation-id",
+ String.valueOf(domain.getGenerationID())));
+
+ /*
+ * Add import/export monitoring attribute
+ */
+ if (domain.importInProgress())
+ {
+ addMonitorData(attributes, "total-update", "import");
+ addMonitorData(
+ attributes, "total-update-entry-count", domain.getTotalEntryCount());
+ addMonitorData(
+ attributes, "total-update-entry-left", domain.getLeftEntryCount());
+ }
+ if (domain.exportInProgress())
+ {
+ addMonitorData(attributes, "total-update", "export");
+ addMonitorData(
+ attributes, "total-update-entry-count", domain.getTotalEntryCount());
+ addMonitorData(
+ attributes, "total-update-entry-left", domain.getLeftEntryCount());
+ }
+
+
+ /* Add the concrete Domain attributes */
+ Collection<Attribute> additionalMonitoring =
+ domain.getAdditionalMonitoring();
+ attributes.addAll(additionalMonitoring);
+
+ /*
+ * Add assured replication related monitoring fields
+ * (see domain.getXXX() method comment for field meaning)
+ */
+
+ addMonitorData(attributes, "assured-sr-sent-updates",
+ domain.getAssuredSrSentUpdates());
+
+ addMonitorData(attributes, "assured-sr-acknowledged-updates",
+ domain.getAssuredSrAcknowledgedUpdates());
+
+ addMonitorData(attributes, "assured-sr-not-acknowledged-updates",
+ domain.getAssuredSrNotAcknowledgedUpdates());
+
+ addMonitorData(attributes, "assured-sr-timeout-updates",
+ domain.getAssuredSrTimeoutUpdates());
+
+ addMonitorData(attributes, "assured-sr-wrong-status-updates",
+ domain.getAssuredSrWrongStatusUpdates());
+
+ addMonitorData(attributes, "assured-sr-replay-error-updates",
+ domain.getAssuredSrReplayErrorUpdates());
+
+ final String ATTR_ASS_SR_SRV = "assured-sr-server-not-acknowledged-updates";
+ type = DirectoryServer.getDefaultAttributeType(ATTR_ASS_SR_SRV);
+ builder = new AttributeBuilder(type, ATTR_ASS_SR_SRV);
+ Map<Short, Integer> srSrvNotAckUps =
+ domain.getAssuredSrServerNotAcknowledgedUpdates();
+ if (srSrvNotAckUps.size() > 0)
+ {
+ for (Short serverId : srSrvNotAckUps.keySet())
+ {
+ String str = serverId + ":" + srSrvNotAckUps.get(serverId);
+ builder.add(new AttributeValue(type, str));
+ }
+ attributes.add(builder.toAttribute());
+ }
+
+ addMonitorData(attributes, "assured-sd-sent-updates",
+ domain.getAssuredSdSentUpdates());
+
+ addMonitorData(attributes, "assured-sd-acknowledged-updates",
+ domain.getAssuredSdAcknowledgedUpdates());
+
+ addMonitorData(attributes, "assured-sd-timeout-updates",
+ domain.getAssuredSdTimeoutUpdates());
+
+ final String ATTR_ASS_SD_SRV = "assured-sd-server-timeout-updates";
+ type = DirectoryServer.getDefaultAttributeType(ATTR_ASS_SD_SRV);
+ builder = new AttributeBuilder(type, ATTR_ASS_SD_SRV);
+ Map<Short, Integer> sdSrvTimUps =
+ domain.getAssuredSdServerTimeoutUpdates();
+ if (sdSrvTimUps.size() > 0)
+ {
+ for (Short serverId : sdSrvTimUps.keySet())
+ {
+ String str = serverId + ":" + sdSrvTimUps.get(serverId);
+ builder.add(new AttributeValue(type, str));
+ }
+ attributes.add(builder.toAttribute());
+ }
+
+ /*
+ * Status related monitoring fields
+ */
+
+ addMonitorData(attributes, "last-status-change-date",
+ domain.getLastStatusChangeDate().toString());
+
+ addMonitorData(attributes, "status", domain.getStatus().toString());
+
+ return attributes;
+
+ }
+
+ /**
+ * Add an attribute with an integer value to the list of monitoring
+ * attributes.
+ *
+ * @param attributes the list of monitoring attributes
+ * @param name the name of the attribute to add.
+ * @param value The integer value of he attribute to add.
+ */
+ public static void addMonitorData(
+ ArrayList<Attribute> attributes,
+ String name,
+ int value)
+ {
+ AttributeType type = DirectoryServer.getDefaultAttributeType(name);
+ attributes.add(Attributes.create(type, new AttributeValue(type,
+ String.valueOf(value))));
+ }
+
+ /**
+ * Add an attribute with an integer value to the list of monitoring
+ * attributes.
+ *
+ * @param attributes the list of monitoring attributes
+ * @param name the name of the attribute to add.
+ * @param value The integer value of he attribute to add.
+ */
+ public static void addMonitorData(
+ ArrayList<Attribute> attributes,
+ String name,
+ long value)
+ {
+ AttributeType type = DirectoryServer.getDefaultAttributeType(name);
+ attributes.add(Attributes.create(type, new AttributeValue(type,
+ String.valueOf(value))));
+ }
+
+ /**
+ * Add an attribute with an integer value to the list of monitoring
+ * attributes.
+ *
+ * @param attributes the list of monitoring attributes
+ * @param name the name of the attribute to add.
+ * @param value The String value of he attribute to add.
+ */
+ public static void addMonitorData(
+ ArrayList<Attribute> attributes,
+ String name,
+ String value)
+ {
+ AttributeType type = DirectoryServer.getDefaultAttributeType(name);
+ attributes.add(Attributes.create(type, new AttributeValue(type, value)));
+ }
+
+ /**
+ * Retrieves the length of time in milliseconds that should elapse between
+ * calls to the <CODE>updateMonitorData()</CODE> method. A negative or zero
+ * return value indicates that the <CODE>updateMonitorData()</CODE> method
+ * should not be periodically invoked.
+ *
+ * @return The length of time in milliseconds that should elapse between
+ * calls to the <CODE>updateMonitorData()</CODE> method.
+ */
+ @Override
+ public long getUpdateInterval()
+ {
+ /* we don't wont to do polling on this monitor */
+ return 0;
+ }
+
+ /**
+ * Performs any processing periodic processing that may be desired to update
+ * the information associated with this monitor. Note that best-effort
+ * attempts will be made to ensure that calls to this method come
+ * <CODE>getUpdateInterval()</CODE> milliseconds apart, but no guarantees will
+ * be made.
+ */
+ @Override
+ public void updateMonitorData()
+ {
+ // As long as getUpdateInterval() returns 0, this will never get called
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/SetGenerationIdTask.java
similarity index 87%
rename from opendj-sdk/opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java
rename to opendj-sdk/opends/src/server/org/opends/server/replication/service/SetGenerationIdTask.java
index f6b9a84..e465800 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/SetGenerationIdTask.java
@@ -24,10 +24,13 @@
*
* Copyright 2006-2008 Sun Microsystems, Inc.
*/
-package org.opends.server.tasks;
+package org.opends.server.replication.service;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.core.DirectoryServer.getAttributeType;
+import org.opends.server.tasks.TaskUtils;
+
+
import java.util.List;
import org.opends.messages.MessageBuilder;
@@ -37,10 +40,8 @@
import org.opends.server.backends.task.TaskState;
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.replication.plugin.ReplicationDomain;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
-import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.ResultCode;
@@ -56,14 +57,9 @@
* The tracer object for the debug logger.
*/
private static final DebugTracer TRACER = getTracer();
-
- boolean isCompressed = false;
- boolean isEncrypted = false;
- boolean skipSchemaValidation = false;
- String domainString = null;
- ReplicationDomain domain = null;
- TaskState initState;
- Long generationId = null;
+ private String domainString = null;
+ private ReplicationDomain domain = null;
+ private Long generationId = null;
private static final void debugInfo(String s)
{
@@ -124,22 +120,18 @@
attrList = taskEntry.getAttribute(typeDomainBase);
domainString = TaskUtils.getSingleValueString(attrList);
- DN domainDN = DN.nullDN();
+
try
{
- domainDN = DN.decode(domainString);
+ domain = ReplicationDomain.retrievesReplicationDomain(domainString);
}
- catch(Exception e)
+ catch(DirectoryException e)
{
MessageBuilder mb = new MessageBuilder();
mb.append(TaskMessages.ERR_TASK_INITIALIZE_INVALID_DN.get());
mb.append(e.getMessage());
- throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
- mb.toMessage());
+ throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, e);
}
-
- domain = ReplicationDomain.retrievesReplicationDomain(domainDN);
-
}
/**
@@ -148,7 +140,7 @@
protected TaskState runTask()
{
debugInfo("setGenerationIdTask is starting on domain%s" +
- domain.getBaseDN());
+ domain.getServiceID());
try
{
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/AckMessageListComparator.java b/opendj-sdk/opends/src/server/org/opends/server/replication/service/package-info.java
similarity index 65%
rename from opendj-sdk/opends/src/server/org/opends/server/replication/server/AckMessageListComparator.java
rename to opendj-sdk/opends/src/server/org/opends/server/replication/service/package-info.java
index fa93ed5..d8708ed 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/AckMessageListComparator.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/service/package-info.java
@@ -22,22 +22,20 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2008 Sun Microsystems, Inc.
+ * Copyright 2008 Sun Microsystems, Inc.
*/
-package org.opends.server.replication.server;
-import java.util.Comparator;
+
/**
- * This comparator is used to build TreeSet of AckMessageLists.
+ * This package contains the generic of the Multi-Master
+ * replication code that works on the Directory Server side.
+ * <br>
+ * Developers planning to use the Replication Service should
+ * subClass the <A> HREF="ReplicationDomain.html"><B>ReplicationDomain</B></A>
+ * class
*/
-public class AckMessageListComparator implements Comparator<AckMessageList>
-{
- /**
- * {@inheritDoc}
- */
- public int compare(AckMessageList a1, AckMessageList a2)
- {
- return a1.getChangeNumber().compareTo(a2.getChangeNumber());
- }
-}
+@org.opends.server.types.PublicAPI(
+ stability=org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.replication.service;
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliMain.java b/opendj-sdk/opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliMain.java
index 9631da3..b26e558 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliMain.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliMain.java
@@ -6129,7 +6129,7 @@
oc.add("ds-task-reset-generation-id");
attrs.put(oc);
attrs.put("ds-task-class-name",
- "org.opends.server.tasks.SetGenerationIdTask");
+ "org.opends.server.replication.service.SetGenerationIdTask");
if (isPre)
{
if (!localOnly)
@@ -6300,7 +6300,7 @@
oc.add("ds-task-initialize-remote-replica");
attrs.put(oc);
attrs.put("ds-task-class-name",
- "org.opends.server.tasks.InitializeTargetTask");
+ "org.opends.server.replication.service.InitializeTargetTask");
attrs.put("ds-task-initialize-domain-dn", baseDN);
attrs.put("ds-task-initialize-replica-server-id", "all");
while (!taskCreated)
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ChangeNumberControlPluginTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ChangeNumberControlPluginTestCase.java
index b0bd7b4..43847cf 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ChangeNumberControlPluginTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ChangeNumberControlPluginTestCase.java
@@ -31,16 +31,12 @@
import java.io.FileReader;
import java.net.ServerSocket;
import org.opends.server.TestCaseUtils;
-import org.opends.server.core.AddOperationBasis;
-import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.replication.ReplicationTestCase;
import org.testng.annotations.DataProvider;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.opends.server.tools.LDAPModify;
import org.opends.server.types.DN;
-import org.opends.server.types.Entry;
-import org.opends.server.types.ResultCode;
import static org.opends.server.util.ServerConstants.*;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@@ -53,19 +49,17 @@
* The port of the replicationServer.
*/
private int replServerPort;
-
+
/**
* The replicationServer that will be used in this test.
*/
- private static final int WINDOW_SIZE = 10;
- private static final int REPLICATION_QUEUE_SIZE = 100;
private DN baseDn;
-
+
/**
* Before starting the tests, start the server and configure a
* replicationServer.
*/
-
+
@BeforeClass(alwaysRun=true)
public void setUp() throws Exception {
super.setUp();
@@ -80,9 +74,9 @@
// replication server
String replServerLdif =
"dn: cn=Replication Server, " + SYNCHRO_PLUGIN_DN + "\n"
- + "objectClass: top\n"
+ + "objectClass: top\n"
+ "objectClass: ds-cfg-replication-server\n"
- + "cn: Replication Server\n"
+ + "cn: Replication Server\n"
+ "ds-cfg-replication-port: " + replServerPort + "\n"
+ "ds-cfg-replication-db-directory: ChangeNumberControlDbTest\n"
+ "ds-cfg-replication-server-id: 103\n";
@@ -128,7 +122,7 @@
+ "changetype: delete"}
};
}
-
+
@Test(dataProvider="operations")
public void ChangeNumberControlTest(String request) throws Exception {
@@ -146,13 +140,13 @@
};
String resultPath = TestCaseUtils.createTempFile();
-
+
FileOutputStream fos = new FileOutputStream(resultPath);
assertEquals(LDAPModify.mainModify(args, false, fos, System.err), 0);
//fos.flush();
fos.close();
-
+
assertTrue(isCsnLinePresent(resultPath));
}
@@ -168,27 +162,5 @@
}
return (found);
}
-
- /**
- * Utility function. Can be used to create and add and entry
- * in the local DS from its ldif description.
- *
- * @param entryString The entry in ldif from.
- * @return The ResultCode of the operation.
- * @throws Exception If something went wrong.
- */
- private ResultCode addEntry(String entryString) throws Exception
- {
- Entry entry;
- AddOperationBasis addOp;
- entry = TestCaseUtils.entryFromLdifString(entryString);
- addOp = new AddOperationBasis(InternalClientConnection.getRootConnection(),
- InternalClientConnection.nextOperationID(), InternalClientConnection
- .nextMessageID(), null, entry.getDN(), entry.getObjectClasses(),
- entry.getUserAttributes(), entry.getOperationalAttributes());
- addOp.setInternalOperation(true);
- addOp.run();
- return addOp.getResultCode();
- }
}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java
index 06c1deb..f73adac 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java
@@ -39,11 +39,11 @@
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.backends.MemoryBackend;
import org.opends.server.core.DirectoryServer;
+import org.opends.server.replication.service.ReplicationBroker;
import org.opends.server.replication.common.ChangeNumberGenerator;
import org.opends.server.replication.plugin.DomainFakeCfg;
import org.opends.server.replication.plugin.MultimasterReplication;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
import org.opends.server.replication.protocol.AddMsg;
import org.opends.server.replication.protocol.DeleteMsg;
import org.opends.server.replication.protocol.ModifyDNMsg;
@@ -90,7 +90,7 @@
public void addModDelDependencyTest() throws Exception
{
ReplicationServer replServer = null;
- ReplicationDomain domain = null;
+ LDAPReplicationDomain domain = null;
DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
SynchronizationProvider replicationPlugin = null;
short brokerId = 2;
@@ -255,7 +255,7 @@
public void moddnDelDependencyTest() throws Exception
{
ReplicationServer replServer = null;
- ReplicationDomain domain = null;
+ LDAPReplicationDomain domain = null;
DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
SynchronizationProvider replicationPlugin = null;
short brokerId = 2;
@@ -302,7 +302,7 @@
Thread.sleep(2000);
domain = MultimasterReplication.createNewDomain(domainConf);
replicationPlugin.completeSynchronizationProvider();
-
+
ReplicationBroker broker =
openReplicationSession(baseDn, brokerId, 1000, replServerPort, 1000,
false, CLEAN_DB_GENERATION_ID);
@@ -330,7 +330,7 @@
assertTrue(found, "The initial entry add failed");
- // disable the domain to make sure that the messages are
+ // disable the domain to make sure that the messages are
// all sent in a row.
domain.disable();
@@ -413,7 +413,7 @@
public void addDelAddDependencyTest() throws Exception
{
ReplicationServer replServer = null;
- ReplicationDomain domain = null;
+ LDAPReplicationDomain domain = null;
DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
SynchronizationProvider replicationPlugin = null;
short brokerId = 2;
@@ -546,7 +546,7 @@
public void addModdnDependencyTest() throws Exception
{
ReplicationServer replServer = null;
- ReplicationDomain domain = null;
+ LDAPReplicationDomain domain = null;
DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
SynchronizationProvider replicationPlugin = null;
short brokerId = 2;
@@ -555,7 +555,7 @@
int AddSequenceLength = 30;
cleanDB();
-
+
try
{
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/GenerationIdTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/GenerationIdTest.java
index db46c73..2bdca58 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/GenerationIdTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/GenerationIdTest.java
@@ -54,10 +54,10 @@
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.replication.service.ReplicationBroker;
import org.opends.server.replication.common.ChangeNumberGenerator;
import org.opends.server.replication.common.ServerStatus;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
import org.opends.server.replication.protocol.AddMsg;
import org.opends.server.replication.protocol.ChangeStatusMsg;
import org.opends.server.replication.protocol.DoneMsg;
@@ -66,7 +66,6 @@
import org.opends.server.replication.protocol.InitializeTargetMsg;
import org.opends.server.replication.protocol.ReplicationMsg;
import org.opends.server.replication.protocol.SocketSession;
-import org.opends.server.replication.protocol.TopologyMsg;
import org.opends.server.replication.server.ReplServerFakeConfiguration;
import org.opends.server.replication.server.ReplicationBackend;
import org.opends.server.replication.server.ReplicationServer;
@@ -118,7 +117,7 @@
private ReplicationServer replServer2 = null;
private ReplicationServer replServer3 = null;
private boolean emptyOldChanges = true;
- ReplicationDomain replDomain = null;
+ LDAPReplicationDomain replDomain = null;
private Entry taskInitRemoteS2;
SocketSession ssSession = null;
boolean ssShutdownRequested = false;
@@ -210,7 +209,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
"ds-task-initialize-domain-dn: " + baseDn,
"ds-task-initialize-replica-server-id: " + server2ID);
}
@@ -440,7 +439,7 @@
"Unable to add the synchronized server");
configEntryList.add(synchroServerEntry.getDN());
- replDomain = ReplicationDomain.retrievesReplicationDomain(baseDn);
+ replDomain = LDAPReplicationDomain.retrievesReplicationDomain(baseDn);
if (replDomain != null)
@@ -668,7 +667,7 @@
}
}
}
- assertEquals(searchOperation.getSearchEntries().size(), expectedCount);
+ assertEquals(searchOperation.getSearchEntries().size(), expectedCount);
}
catch(Exception e)
{
@@ -719,14 +718,14 @@
debugInfo(testCase + " Expect genId to be set in memory on the replication " +
" server side (not wrote on disk/db since no change occurred).");
- rgenId = replServer1.getGenerationId(baseDn);
+ rgenId = replServer1.getGenerationId(baseDn.toNormalizedString());
assertEquals(rgenId, EMPTY_DN_GENID);
// Clean for next test
debugInfo(testCase + " Unconfiguring DS1 to replicate to RS1(" + changelog1ID + ")");
disconnectFromReplServer(changelog1ID);
-
+
//===========================================================
debugInfo(testCase + " ** TEST ** Non empty backend");
@@ -742,7 +741,7 @@
assertTrue(genId != EMPTY_DN_GENID);
debugInfo(testCase + " Test that the generationId is set on RS1");
- rgenId = replServer1.getGenerationId(baseDn);
+ rgenId = replServer1.getGenerationId(baseDn.toNormalizedString());
assertEquals(genId, rgenId);
//===========================================================
@@ -771,22 +770,6 @@
fail("Broker connection is expected to be accepted.");
}
- // Broker 2 should receive an update topo message to signal broker 3
- // arrival in topology
- try
- {
- ReplicationMsg msg = broker2.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 2 connection is expected to receive a TopologyMsg."
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS2 is expected to receive a TopologyMsg.");
- }
-
//===========================================================
debugInfo(testCase + " ** TEST ** DS2 (bad genID) changes must be ignored.");
@@ -826,7 +809,8 @@
//===========================================================
debugInfo(testCase + " ** TEST ** Persistence of the generation ID in RS1");
- long genIdBeforeShut = replServer1.getGenerationId(baseDn);
+ long genIdBeforeShut =
+ replServer1.getGenerationId(baseDn.toNormalizedString());
debugInfo("Shutdown replServer1");
broker2.stop();
@@ -847,7 +831,8 @@
debugInfo("Delay to allow DS to reconnect to replServer1");
- long genIdAfterRestart = replServer1.getGenerationId(baseDn);
+ long genIdAfterRestart =
+ replServer1.getGenerationId(baseDn.toNormalizedString());
debugInfo("Aft restart / replServer.genId=" + genIdAfterRestart);
assertTrue(replServer1!=null, "Replication server creation failed.");
assertTrue(genIdBeforeShut == genIdAfterRestart,
@@ -880,21 +865,6 @@
fail("Broker connection is expected to be accepted.");
}
- // Broker 2 should receive an update topo message to signal broker 3
- // arrival in topology
- try
- {
- ReplicationMsg msg = broker2.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 2 connection is expected to receive a TopologyMsg."
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS2 is expected to receive a TopologyMsg.");
- }
debugInfo("Launch on-line import on DS1");
long oldGenId = genId;
@@ -903,69 +873,6 @@
performLdifImport();
connectServer1ToChangelog(changelog1ID);
- // Broker 2 and 3 should receive 2 update topo messages to signal broker 1
- // has disconnected from topology then reconnected
- try
- {
- ReplicationMsg msg = broker2.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 2 connection is expected to receive a first TopologyMsg" +
- " for Broker 1 import + reconnect."
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS2 is expected to receive a first TopologyMsg for DS1 import + " +
- "reconnect.");
- }
- try
- {
- ReplicationMsg msg = broker3.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 3 connection is expected to receive a first TopologyMsg" +
- " for Broker 1 import + reconnect."
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS3 is expected to receive a first TopologyMsg for DS1 import + " +
- "reconnect.");
- }
- try
- {
- ReplicationMsg msg = broker2.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 2 connection is expected to receive a second TopologyMsg" +
- " for Broker 1 import + reconnect."
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS2 is expected to receive a second TopologyMsg for DS1 import + " +
- "reconnect.");
- }
- try
- {
- ReplicationMsg msg = broker3.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 3 connection is expected to receive a second TopologyMsg" +
- " for Broker 1 import + reconnect."
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS3 is expected to receive a second TopologyMsg for DS1 import + " +
- "reconnect.");
- }
-
debugInfo("Create Reset task on DS1 to propagate the new gen ID as the reference");
Entry taskReset = TestCaseUtils.makeEntry(
"dn: ds-task-id=resetgenid"+genId+ UUID.randomUUID() +
@@ -973,10 +880,10 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-reset-generation-id",
- "ds-task-class-name: org.opends.server.tasks.SetGenerationIdTask",
+ "ds-task-class-name: org.opends.server.replication.service.SetGenerationIdTask",
"ds-task-reset-generation-id-domain-base-dn: " + baseDnStr);
addTask(taskReset, ResultCode.SUCCESS, null);
- waitTaskState(taskReset, TaskState.COMPLETED_SUCCESSFULLY, null);
+ waitTaskState(taskReset, TaskState.COMPLETED_SUCCESSFULLY, null);
// Broker 2 and 3 should receive 1 change status message to order them
// to enter the bad gen id status
@@ -1025,66 +932,6 @@
"bad gen id status.");
}
- // Broker 2 and 3 should receive 1 update topo message to signal other broker gen id
- // has been resetted, and 2 topo messages to signal DS1 has been disconnected
- // then reconnected
- try
- {
- ReplicationMsg msg = broker2.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 2 connection is expected to receive 1 TopologyMsg" +
- " for gen id reset of broker 3."
- + msg);
- }
- msg = broker2.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 2 connection is expected to receive 1 TopologyMsg" +
- " for DS1 disconnected."
- + msg);
- }
- msg = broker2.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 2 connection is expected to receive 1 TopologyMsg" +
- " for DS1 reconnected."
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS2 is expected to receive 3 TopologyMsg for gen id reset.");
- }
- try
- {
- ReplicationMsg msg = broker3.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 3 connection is expected to receive 1 TopologyMsg" +
- " for gen id reset of broker 2."
- + msg);
- }
- msg = broker3.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 3 connection is expected to receive 1 TopologyMsg" +
- " for DS1 disconnected."
- + msg);
- }
- msg = broker3.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 3 connection is expected to receive 1 TopologyMsg" +
- " for DS1 reconnected"
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS3 is expected to receive 3 TopologyMsg for gen id reset.");
- }
-
debugInfo("DS1 root entry must contain the new gen ID");
genId = readGenIdFromSuffixRootEntry();
assertTrue(genId != -1, "DS is expected to have a new genID computed " +
@@ -1093,13 +940,14 @@
+ "is expected to be diffrent from previous one");
debugInfo("RS1 must have the new gen ID");
- rgenId = replServer1.getGenerationId(baseDn);
+ rgenId = replServer1.getGenerationId(baseDn.toNormalizedString());
assertEquals(genId, rgenId, "DS and replServer are expected to have same genId.");
debugInfo("RS1 must have been cleared since it has not the proper generation ID");
checkChangelogSize(0);
- assertTrue(!replServer1.getReplicationServerDomain(baseDn, false).
+ assertTrue(!replServer1.getReplicationServerDomain(
+ baseDn.toNormalizedString(), false).
isDegradedDueToGenerationId(server1ID),
"Expecting that DS1 status in RS1 is : not in bad gen id.");
@@ -1107,10 +955,12 @@
debugInfo(testCase + " ** TEST ** Previous test set a new gen ID on the "+
"topology, verify degradation of DS2 and DS3");
- assertTrue(replServer1.getReplicationServerDomain(baseDn, false).
+ assertTrue(replServer1.getReplicationServerDomain(
+ baseDn.toNormalizedString(), false).
isDegradedDueToGenerationId(server2ID),
"Expecting that DS2 with old gen ID is in bad gen id from RS1");
- assertTrue(replServer1.getReplicationServerDomain(baseDn, false).
+ assertTrue(replServer1.getReplicationServerDomain(
+ baseDn.toNormalizedString(), false).
isDegradedDueToGenerationId(server3ID),
"Expecting that DS3 with old gen ID is in bad gen id from RS1");
@@ -1173,35 +1023,6 @@
broker3 = openReplicationSession(baseDn,
server3ID, 100, getChangelogPort(changelog1ID), 1000, emptyOldChanges, genId);
- // Broker 2 should receive 2 update topo messages to signal broker 3
- // stopped and restarted
- try
- {
- ReplicationMsg msg = broker2.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 2 connection is expected to receive a first TopologyMsg."
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS2 is expected to receive a first TopologyMsg.");
- }
- try
- {
- ReplicationMsg msg = broker2.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 2 connection is expected to receive a second TopologyMsg."
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS2 is expected to receive a second TopologyMsg.");
- }
-
debugInfo("Adding reset task to DS1");
taskReset = TestCaseUtils.makeEntry(
"dn: ds-task-id=resetgenid"+ UUID.randomUUID() +
@@ -1209,47 +1030,15 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-reset-generation-id",
- "ds-task-class-name: org.opends.server.tasks.SetGenerationIdTask",
+ "ds-task-class-name: org.opends.server.replication.service.SetGenerationIdTask",
"ds-task-reset-generation-id-domain-base-dn: " + baseDnStr);
addTask(taskReset, ResultCode.SUCCESS, null);
waitTaskState(taskReset, TaskState.COMPLETED_SUCCESSFULLY, null);
Thread.sleep(200);
- // Broker 2 and 3 should not receive a change status as they are connected with
- // the right genid, but should anyway receive a topo message to update them with potential
- // updates.
- try
- {
- ReplicationMsg msg = broker2.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 2 connection is expected to receive 1 TopologyMsg" +
- " for topo update on reset gen id."
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS2 is expected to receive 1 TopologyMsg for topo update on reset gen id.");
- }
- try
- {
- ReplicationMsg msg = broker3.receive();
- if (!(msg instanceof TopologyMsg))
- {
- fail("Broker 3 connection is expected to receive 1 TopologyMsg" +
- " for topo update on reset gen id."
- + msg);
- }
- }
- catch(SocketTimeoutException se)
- {
- fail("DS3 is expected to receive 1 TopologyMsg for topo update on reset gen id.");
- }
-
debugInfo("Verify that RS1 has still the right genID");
- assertEquals(replServer1.getGenerationId(baseDn), rgenId);
+ assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), rgenId);
// Updates count in RS1 must stay unchanged = to 1
Thread.sleep(500);
@@ -1257,13 +1046,15 @@
debugInfo("Verifying that DS2 is not in bad gen id any more");
- assertTrue(!replServer1.getReplicationServerDomain(baseDn, false).
+ assertTrue(!replServer1.getReplicationServerDomain(
+ baseDn.toNormalizedString(), false).
isDegradedDueToGenerationId(server2ID),
"Expecting that DS2 is not in bad gen id from RS1");
debugInfo("Verifying that DS3 is not in bad gen id any more");
- assertTrue(!replServer1.getReplicationServerDomain(baseDn, false).
+ assertTrue(!replServer1.getReplicationServerDomain(
+ baseDn.toNormalizedString(), false).
isDegradedDueToGenerationId(server3ID),
"Expecting that DS3 is not in bad gen id from RS1");
@@ -1281,7 +1072,7 @@
/* expected */
AddMsg rcvmsg = (AddMsg)msg;
assertEquals(rcvmsg.getChangeNumber(), emsg.getChangeNumber());
- }
+ }
catch(SocketTimeoutException e)
{
fail("The msg send by DS2 is expected to be received by DS3)");
@@ -1339,11 +1130,11 @@
Thread.sleep(1500);
debugInfo("Expect genId are set in all replServers.");
- assertEquals(replServer1.getGenerationId(baseDn), EMPTY_DN_GENID,
+ assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), EMPTY_DN_GENID,
" in replServer1");
- assertEquals(replServer2.getGenerationId(baseDn), EMPTY_DN_GENID,
+ assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), EMPTY_DN_GENID,
" in replServer2");
- assertEquals(replServer3.getGenerationId(baseDn), EMPTY_DN_GENID,
+ assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), EMPTY_DN_GENID,
" in replServer3");
debugInfo("Disconnect DS from replServer1.");
@@ -1352,9 +1143,9 @@
debugInfo(
"Expect genIds to be resetted in all servers to -1 as no more DS in topo");
- assertEquals(replServer1.getGenerationId(baseDn), -1);
- assertEquals(replServer2.getGenerationId(baseDn), -1);
- assertEquals(replServer3.getGenerationId(baseDn), -1);
+ assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), -1);
+ assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), -1);
+ assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), -1);
debugInfo("Add entries to DS");
this.addTestEntriesToDB(updatedEntries);
@@ -1367,9 +1158,9 @@
"Expect genIds to be set in all servers based on the added entries.");
genId = readGenIdFromSuffixRootEntry();
assertTrue(genId != -1);
- assertEquals(replServer1.getGenerationId(baseDn), genId);
- assertEquals(replServer2.getGenerationId(baseDn), genId);
- assertEquals(replServer3.getGenerationId(baseDn), genId);
+ assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), genId);
+ assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), genId);
+ assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), genId);
debugInfo("Connecting broker2 to replServer3 with a good genId");
try
@@ -1385,7 +1176,7 @@
debugInfo(
"Expecting that broker2 is not in bad gen id since it has a correct genId");
- assertTrue(!replServer1.getReplicationServerDomain(baseDn, false).
+ assertTrue(!replServer1.getReplicationServerDomain(baseDn.toNormalizedString(), false).
isDegradedDueToGenerationId(server2ID));
debugInfo("Disconnecting DS from replServer1");
@@ -1393,9 +1184,9 @@
debugInfo(
"Expect all genIds to keep their value since broker2 is still connected.");
- assertEquals(replServer1.getGenerationId(baseDn), genId);
- assertEquals(replServer2.getGenerationId(baseDn), genId);
- assertEquals(replServer3.getGenerationId(baseDn), genId);
+ assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), genId);
+ assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), genId);
+ assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), genId);
debugInfo("Connecting broker2 to replServer1 with a bad genId");
try
@@ -1412,7 +1203,7 @@
debugInfo(
"Expecting that broker3 is in bad gen id since it has a bad genId");
- assertTrue(replServer1.getReplicationServerDomain(baseDn, false).
+ assertTrue(replServer1.getReplicationServerDomain(baseDn.toNormalizedString(), false).
isDegradedDueToGenerationId(server3ID));
int found = testEntriesInDb();
@@ -1432,7 +1223,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-reset-generation-id",
- "ds-task-class-name: org.opends.server.tasks.SetGenerationIdTask",
+ "ds-task-class-name: org.opends.server.replication.service.SetGenerationIdTask",
"ds-task-reset-generation-id-domain-base-dn: " + baseDnStr);
addTask(taskReset, ResultCode.SUCCESS, null);
waitTaskState(taskReset, TaskState.COMPLETED_SUCCESSFULLY, null);
@@ -1440,9 +1231,9 @@
debugInfo("Verifying that all replservers genIds have been reset.");
genId = readGenIdFromSuffixRootEntry();
- assertEquals(replServer1.getGenerationId(baseDn), genId);
- assertEquals(replServer2.getGenerationId(baseDn), genId);
- assertEquals(replServer3.getGenerationId(baseDn), genId);
+ assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), genId);
+ assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), genId);
+ assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), genId);
debugInfo("Adding reset task to DS.");
taskReset = TestCaseUtils.makeEntry(
@@ -1451,7 +1242,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-reset-generation-id",
- "ds-task-class-name: org.opends.server.tasks.SetGenerationIdTask",
+ "ds-task-class-name: org.opends.server.replication.service.SetGenerationIdTask",
"ds-task-reset-generation-id-domain-base-dn: " + baseDnStr,
"ds-task-reset-generation-id-new-value: -1");
addTask(taskReset, ResultCode.SUCCESS, null);
@@ -1460,9 +1251,9 @@
debugInfo("Verifying that all replservers genIds have been reset.");
genId = readGenIdFromSuffixRootEntry();
- assertEquals(replServer1.getGenerationId(baseDn), -1);
- assertEquals(replServer2.getGenerationId(baseDn), -1);
- assertEquals(replServer3.getGenerationId(baseDn), -1);
+ assertEquals(replServer1.getGenerationId(baseDn.toNormalizedString()), -1);
+ assertEquals(replServer2.getGenerationId(baseDn.toNormalizedString()), -1);
+ assertEquals(replServer3.getGenerationId(baseDn.toNormalizedString()), -1);
debugInfo(
"Disconnect DS from replServer1 (required in order to DEL entries).");
@@ -1579,8 +1370,8 @@
}
/**
- * Loop opening sessions to the Replication Server
- * to check that it handle correctly deconnection and reconnection.
+ * Loop opening sessions to the Replication Server
+ * to check that it handle correctly deconnection and reconnection.
*/
@Test(enabled=false, groups="slow")
public void testLoop() throws Exception
@@ -1604,10 +1395,10 @@
long generationId = 1000+i;
broker = openReplicationSession(baseDn,
server2ID, 100, getChangelogPort(changelog1ID),
- 1000, !emptyOldChanges, generationId);
+ 1000, !emptyOldChanges, generationId);
debugInfo(testCase + " Expect genId to be set in memory on the replication " +
- " server side even if not wrote on disk/db since no change occurred.");
- rgenId = replServer1.getGenerationId(baseDn);
+ " server side even if not wrote on disk/db since no change occurred.");
+ rgenId = replServer1.getGenerationId(baseDn.toNormalizedString());
assertEquals(rgenId, generationId);
broker.stop();
broker = null;
@@ -1620,9 +1411,9 @@
postTest();
}
}
-
+
/**
- * This is used to make sure that the 3 tests are run in the
+ * This is used to make sure that the 3 tests are run in the
* specified order since this is necessary.
*/
@Test(enabled=true, groups="slow")
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java
index 7713a48..443b601 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java
@@ -60,8 +60,8 @@
import org.opends.messages.Severity;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.service.ReplicationBroker;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
import org.opends.server.replication.protocol.DoneMsg;
import org.opends.server.replication.protocol.EntryMsg;
import org.opends.server.replication.protocol.ErrorMsg;
@@ -144,7 +144,7 @@
ReplicationServer changelog2 = null;
ReplicationServer changelog3 = null;
boolean emptyOldChanges = true;
- ReplicationDomain replDomain = null;
+ LDAPReplicationDomain replDomain = null;
private void log(String s)
{
@@ -181,7 +181,7 @@
// re-enabled and this clears the backend reference and thus the underlying
// data. So for this particular test, we use a classical backend. Let's
// clear it.
- ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+ LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
updatedEntries = newLDIFEntries();
@@ -190,7 +190,7 @@
connection = InternalClientConnection.getRootConnection();
synchroServerEntry = null;
- replServerEntry = null;
+ replServerEntry = null;
taskInitFromS2 = TestCaseUtils.makeEntry(
"dn: ds-task-id=" + UUID.randomUUID() +
@@ -198,7 +198,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-from-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
"ds-task-initialize-domain-dn: " + EXAMPLE_DN,
"ds-task-initialize-replica-server-id: " + server2ID);
@@ -208,7 +208,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
"ds-task-initialize-domain-dn: " + EXAMPLE_DN,
"ds-task-initialize-replica-server-id: " + server2ID);
@@ -218,7 +218,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
"ds-task-initialize-domain-dn: " + EXAMPLE_DN,
"ds-task-initialize-replica-server-id: all");
}
@@ -321,7 +321,7 @@
{
break;
}
- Thread.sleep(10);
+ Thread.sleep(100);
}
} while (completionTime == null);
@@ -485,8 +485,10 @@
// Send entries
try
{
- RoutableMsg initTargetMessage = new InitializeTargetMsg(
- baseDn, server2ID, destinationServerID, requestorID, updatedEntries.length);
+ RoutableMsg initTargetMessage =
+ new InitializeTargetMsg(
+ EXAMPLE_DN, server2ID, destinationServerID, requestorID,
+ updatedEntries.length);
broker.publish(initTargetMessage);
for (String entry : updatedEntries)
@@ -536,7 +538,7 @@
{
EntryMsg em = (EntryMsg)msg;
log("Broker " + serverID + " receives entry " + new String(em.getEntryBytes()));
- entriesReceived++;
+ entriesReceived+=countEntryLimits(em.getEntryBytes());
}
else if (msg instanceof DoneMsg)
{
@@ -572,6 +574,30 @@
}
/**
+ * Count the number of entries in the provided byte[].
+ * This is based on the hypothesis that the entries are separated
+ * by a "\n\n" String.
+ *
+ * @param entryBytes
+ * @return The number of entries in the provided byte[].
+ */
+ private int countEntryLimits(byte[] entryBytes)
+ {
+ int entryCount = 0;
+ int count = 0;
+ while (count<=entryBytes.length-2)
+ {
+ if ((entryBytes[count] == '\n') && (entryBytes[count+1] == '\n'))
+ {
+ entryCount++;
+ count++;
+ }
+ count++;
+ }
+ return entryCount;
+ }
+
+ /**
* Creates a new replicationServer.
* @param changelogId The serverID of the replicationServer to create.
* @return The new replicationServer.
@@ -592,7 +618,7 @@
ReplServerFakeConfiguration conf =
new ReplServerFakeConfiguration(
getChangelogPort(changelogId),
- "initOnlineTest" + getChangelogPort(changelogId) + testCase + "Db",
+ "initOnlineTest" + getChangelogPort(changelogId) + testCase + "Db",
0,
changelogId,
0,
@@ -638,7 +664,7 @@
// Clear the backend
- ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+ LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
synchroServerEntry = TestCaseUtils.entryFromLdifString(synchroServerLdif);
DirectoryServer.getConfigHandler().addEntry(synchroServerEntry, null);
@@ -646,7 +672,7 @@
"Unable to add the synchronized server");
configEntryList.add(synchroServerEntry.getDN());
- replDomain = ReplicationDomain.retrievesReplicationDomain(baseDn);
+ replDomain = LDAPReplicationDomain.retrievesReplicationDomain(baseDn);
assertTrue(!replDomain.ieRunning(),
"ReplicationDomain: Import/Export is not expected to be running");
@@ -699,7 +725,7 @@
server2 = openReplicationSession(DN.decode(EXAMPLE_DN),
server2ID, 100, getChangelogPort(changelog1ID), 1000, emptyOldChanges);
- Thread.sleep(2000);
+ // Thread.sleep(2000);
// In S1 launch the total update
addTask(taskInitFromS2, ResultCode.SUCCESS, null);
@@ -729,7 +755,7 @@
catch(Exception e)
{
fail(testCase + " Exception:"+ e.getMessage() + " " + stackTraceToSingleLineString(e));
- } finally
+ } finally
{
afterTest();
}
@@ -744,7 +770,7 @@
String testCase = "initializeExport";
log("Starting "+testCase);
-
+
try
{
changelog1 = createChangelogServer(changelog1ID, testCase);
@@ -758,9 +784,11 @@
server2 = openReplicationSession(DN.decode(EXAMPLE_DN),
server2ID, 100, getChangelogPort(changelog1ID), 1000, emptyOldChanges);
- Thread.sleep(3000);
+ // Not needed anymore since OpenReplicationSession
+ // checks for session establishment ?
+ // Thread.sleep(3000);
- InitializeRequestMsg initMsg = new InitializeRequestMsg(baseDn,
+ InitializeRequestMsg initMsg = new InitializeRequestMsg(EXAMPLE_DN,
server2ID, server1ID);
server2.publish(initMsg);
@@ -782,7 +810,7 @@
String testCase = "initializeTargetExport";
log("Starting " + testCase);
-
+
try
{
@@ -799,7 +827,7 @@
server2 = openReplicationSession(DN.decode(EXAMPLE_DN),
server2ID, 100, getChangelogPort(changelog1ID), 1000, emptyOldChanges);
- Thread.sleep(1000);
+ // Thread.sleep(1000);
// Launch in S1 the task that will initialize S2
addTask(taskInitTargetS2, ResultCode.SUCCESS, null);
@@ -846,7 +874,7 @@
server3 = openReplicationSession(DN.decode(EXAMPLE_DN),
server3ID, 100, getChangelogPort(changelog1ID), 1000, emptyOldChanges);
- Thread.sleep(1000);
+ // Thread.sleep(1000);
// Launch in S1 the task that will initialize S2
addTask(taskInitTargetAll, ResultCode.SUCCESS, null);
@@ -891,7 +919,14 @@
// S2 publishes entries to S1
makeBrokerPublishEntries(server2, server2ID, server1ID, server2ID);
- Thread.sleep(10000); // FIXME - how to know the import is done
+ // wait until the replication domain has expected generationID
+ // this should indicate that the import occured correctly.
+ for (int count = 0; count < 100; count++)
+ {
+ if (replDomain.getGenerationID() == 56869)
+ break;
+ Thread.sleep(200);
+ }
// Test that entries have been imported in S1
testEntriesInDb();
@@ -926,7 +961,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
"ds-task-initialize-domain-dn: foo",
"ds-task-initialize-remote-replica-server-id: " + server2ID);
addTask(taskInitTarget, ResultCode.INVALID_DN_SYNTAX,
@@ -939,7 +974,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
"ds-task-initialize-domain-dn: dc=foo",
"ds-task-initialize-remote-replica-server-id: " + server2ID);
addTask(taskInitTarget, ResultCode.OTHER,
@@ -987,11 +1022,11 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-from-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
"ds-task-initialize-domain-dn: foo",
"ds-task-initialize-replica-server-id: " + server2ID);
addTask(taskInit, ResultCode.INVALID_DN_SYNTAX,
- ERR_TASK_INITIALIZE_INVALID_DN.get());
+ ERR_NO_MATCHING_DOMAIN.get());
// Domain base dn not related to any domain
taskInit = TestCaseUtils.makeEntry(
@@ -1000,10 +1035,11 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-from-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
"ds-task-initialize-domain-dn: dc=foo",
"ds-task-initialize-replica-server-id: " + server2ID);
- addTask(taskInit, ResultCode.OTHER, ERR_NO_MATCHING_DOMAIN.get());
+ addTask(taskInit, ResultCode.INVALID_DN_SYNTAX,
+ ERR_NO_MATCHING_DOMAIN.get());
// Invalid Source
taskInit = TestCaseUtils.makeEntry(
@@ -1012,7 +1048,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-from-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
"ds-task-initialize-domain-dn: " + baseDn,
"ds-task-initialize-replica-server-id: -3");
addTask(taskInit, ResultCode.OTHER,
@@ -1083,25 +1119,29 @@
// Check that the list of connected LDAP servers is correct
// in each replication servers
- List<String> l1 = changelog1.getReplicationServerDomain(baseDn, false).
+ List<String> l1 = changelog1.getReplicationServerDomain(
+ baseDn.toNormalizedString(), false).
getConnectedLDAPservers();
assertEquals(l1.size(), 1);
assertEquals(l1.get(0), String.valueOf(server1ID));
List<String> l2;
- l2 = changelog2.getReplicationServerDomain(baseDn, false).getConnectedLDAPservers();
+ l2 = changelog2.getReplicationServerDomain(
+ baseDn.toNormalizedString(), false).getConnectedLDAPservers();
assertEquals(l2.size(), 2);
assertTrue(l2.contains(String.valueOf(server2ID)));
assertTrue(l2.contains(String.valueOf(server3ID)));
List<String> l3;
- l3 = changelog3.getReplicationServerDomain(baseDn, false).getConnectedLDAPservers();
+ l3 = changelog3.getReplicationServerDomain(
+ baseDn.toNormalizedString(), false).getConnectedLDAPservers();
assertEquals(l3.size(), 0);
// Test updates
broker3.stop();
Thread.sleep(1000);
- l2 = changelog2.getReplicationServerDomain(baseDn, false).getConnectedLDAPservers();
+ l2 = changelog2.getReplicationServerDomain(
+ baseDn.toNormalizedString(), false).getConnectedLDAPservers();
assertEquals(l2.size(), 1);
assertEquals(l2.get(0), String.valueOf(server2ID));
@@ -1109,7 +1149,8 @@
server3ID, 100, getChangelogPort(changelog2ID), 1000, emptyOldChanges);
broker2.stop();
Thread.sleep(1000);
- l2 = changelog2.getReplicationServerDomain(baseDn, false).getConnectedLDAPservers();
+ l2 = changelog2.getReplicationServerDomain(
+ baseDn.toNormalizedString(), false).getConnectedLDAPservers();
assertEquals(l2.size(), 1);
assertEquals(l2.get(0), String.valueOf(server3ID));
@@ -1124,7 +1165,7 @@
afterTest();
}
}
-
+
@Test(enabled=true, groups="slow")
public void initializeTargetExportMultiSS() throws Exception
{
@@ -1153,7 +1194,7 @@
server2ID, 100, getChangelogPort(changelog2ID), 1000, emptyOldChanges);
}
- Thread.sleep(1000);
+ // Thread.sleep(1000);
// Launch in S1 the task that will initialize S2
addTask(taskInitTargetS2, ResultCode.SUCCESS, null);
@@ -1200,7 +1241,8 @@
log(testCase + " Will connect server 2 to " + changelog2ID);
server2 = openReplicationSession(DN.decode(EXAMPLE_DN),
server2ID, 100, getChangelogPort(changelog2ID),
- 1000, emptyOldChanges, changelog1.getGenerationId(baseDn));
+ 1000, emptyOldChanges,
+ changelog1.getGenerationId(baseDn.toNormalizedString()));
}
// Connect a broker acting as server 3 to Repl Server 3
@@ -1212,15 +1254,16 @@
log(testCase + " Will connect server 3 to " + changelog3ID);
server3 = openReplicationSession(DN.decode(EXAMPLE_DN),
server3ID, 100, getChangelogPort(changelog3ID),
- 1000, emptyOldChanges, changelog1.getGenerationId(baseDn));
+ 1000, emptyOldChanges,
+ changelog1.getGenerationId(baseDn.toNormalizedString()));
}
- Thread.sleep(500);
+ // Thread.sleep(500);
// S3 sends init request
log(testCase + " server 3 Will send reqinit to " + server1ID);
InitializeRequestMsg initMsg =
- new InitializeRequestMsg(baseDn, server3ID, server1ID);
+ new InitializeRequestMsg(EXAMPLE_DN, server3ID, server1ID);
server3.publish(initMsg);
// S3 should receive target, entries & done
@@ -1268,7 +1311,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-from-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
"ds-task-initialize-domain-dn: " + baseDn,
"ds-task-initialize-replica-server-id: " + 20);
@@ -1284,7 +1327,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-from-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
"ds-task-initialize-domain-dn: " + baseDn,
"ds-task-initialize-replica-server-id: " + server1ID);
@@ -1326,7 +1369,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTargetTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTargetTask",
"ds-task-initialize-domain-dn: " + baseDn,
"ds-task-initialize-replica-server-id: " + 0);
@@ -1400,7 +1443,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-from-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
"ds-task-initialize-domain-dn: " + baseDn,
"ds-task-initialize-replica-server-id: " + server2ID);
@@ -1414,7 +1457,7 @@
"objectclass: top",
"objectclass: ds-task",
"objectclass: ds-task-initialize-from-remote-replica",
- "ds-task-class-name: org.opends.server.tasks.InitializeTask",
+ "ds-task-class-name: org.opends.server.replication.service.InitializeTask",
"ds-task-initialize-domain-dn: " + baseDn,
"ds-task-initialize-replica-server-id: " + server2ID);
@@ -1537,8 +1580,8 @@
super.classCleanUp();
// Clear the backend
- ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
-
+ LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+
paranoiaCheck();
}
}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ProtocolWindowTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ProtocolWindowTest.java
index 8382644..db2083f 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ProtocolWindowTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ProtocolWindowTest.java
@@ -31,6 +31,8 @@
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
+import org.opends.server.replication.service.ReplicationBroker;
+
import java.net.ServerSocket;
import java.net.SocketTimeoutException;
import java.util.Iterator;
@@ -49,7 +51,6 @@
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.ldap.LDAPFilter;
-import org.opends.server.replication.plugin.ReplicationBroker;
import org.opends.server.replication.protocol.AddMsg;
import org.opends.server.replication.protocol.ReplicationMsg;
import org.opends.server.replication.server.ReplServerFakeConfiguration;
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReSyncTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReSyncTest.java
index eb65fe1..2e4dfbc 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReSyncTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReSyncTest.java
@@ -39,14 +39,13 @@
import org.opends.server.core.AddOperationBasis;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ResultCode;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
-import static org.opends.server.TestCaseUtils.*;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.ErrorLogger.logError;
import static org.opends.server.loggers.debug.DebugLogger.getTracer;
@@ -97,7 +96,7 @@
// data. So for this particular test, we use a classical backend. Let's
// clear it and create the root entry
- ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+ LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
addEntry("dn: dc=example,dc=com\n" + "objectClass: top\n"
+ "objectClass: domain\n");
@@ -108,7 +107,7 @@
+ "objectClass: ds-cfg-replication-server\n"
+ "cn: Replication Server\n"
+ "ds-cfg-replication-port:" + replServerPort + "\n"
- + "ds-cfg-replication-db-directory: ReSyncTest\n"
+ + "ds-cfg-replication-db-directory: ReSyncTest\n"
+ "ds-cfg-replication-server-id: 104\n";
replServerEntry = TestCaseUtils.entryFromLdifString(replServerLdif);
@@ -153,7 +152,7 @@
entry.getUserAttributes(), entry.getOperationalAttributes());
addOp.setInternalOperation(true);
addOp.run();
-
+
entryList.add(entry.getDN());
return addOp.getResultCode();
}
@@ -258,7 +257,7 @@
if (getEntry(DN.decode("dc=fooUniqueName2," + EXAMPLE_DN), 30000, true) == null)
fail("The Directory has not been resynchronized after the restore.");
}
-
+
/**
* Clean up the environment.
*
@@ -272,7 +271,7 @@
super.classCleanUp();
// Clear the backend
- ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+ LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
paranoiaCheck();
}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
index 880b11f..f2c0de2 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
@@ -58,10 +58,10 @@
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.ldap.LDAPFilter;
+import org.opends.server.replication.service.ReplicationBroker;
import org.opends.server.replication.common.ServerState;
import org.opends.server.replication.plugin.PersistentServerState;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
import org.opends.server.replication.protocol.ErrorMsg;
import org.opends.server.replication.protocol.ReplSessionSecurity;
import org.opends.server.replication.protocol.ReplicationMsg;
@@ -104,7 +104,7 @@
// TestCaseUtils.initializeTestBackend(true).
// (using the default TestCaseUtils.TEST_ROOT_DN_STRING suffix)
protected static final long TEST_DN_WITH_ROOT_ENTRY_GENID = 5095L;
-
+
/**
* Generation id for a fully empty domain.
*/
@@ -139,8 +139,8 @@
// Call the paranoiaCheck at test cleanup or not.
// Must not been touched except if sub class has its own clean up code,
- // for instance:
- // @AfterClass
+ // for instance:
+ // @AfterClass
// public void classCleanUp() throws Exception
// {
// callParanoiaCheck = false;
@@ -202,8 +202,8 @@
long genId = TEST_DN_WITH_ROOT_ENTRY_GENID;
try
{
- ReplicationDomain replDomain = ReplicationDomain.retrievesReplicationDomain(baseDn);
- genId = replDomain.getGenerationId();
+ LDAPReplicationDomain replDomain = LDAPReplicationDomain.retrievesReplicationDomain(baseDn);
+ genId = replDomain.getGenerationID();
}
catch(Exception e) {}
return genId;
@@ -233,15 +233,14 @@
long generationId)
throws Exception, SocketException
{
- ServerState state;
+ ServerState state = new ServerState();
+
if (emptyOldChanges)
- state = new PersistentServerState(baseDn, serverId);
- else
- state = new ServerState();
+ new PersistentServerState(baseDn, serverId, new ServerState());
ReplicationBroker broker = new ReplicationBroker(null,
- state, baseDn, serverId, 0, 0, 0, 0,
- window_size, 0, generationId, getReplSessionSecurity(), (byte)1);
+ state, baseDn.toNormalizedString(), serverId, window_size,
+ generationId, 100000, getReplSessionSecurity(), (byte)1);
ArrayList<String> servers = new ArrayList<String>(1);
servers.add("localhost:" + port);
broker.start(servers);
@@ -347,8 +346,8 @@
throws Exception, SocketException
{
ReplicationBroker broker = new ReplicationBroker(null,
- state, baseDn, serverId, 0, 0, 0, 0, window_size, 0, generationId,
- getReplSessionSecurity(), (byte)1);
+ state, baseDn.toNormalizedString(), serverId, window_size, generationId,
+ 100000, getReplSessionSecurity(), (byte)1);
ArrayList<String> servers = new ArrayList<String>(1);
servers.add("localhost:" + port);
broker.start(servers);
@@ -380,16 +379,14 @@
boolean emptyOldChanges, long generationId)
throws Exception, SocketException
{
- ServerState state;
+ ServerState state = new ServerState();
+
if (emptyOldChanges)
- state = new PersistentServerState(baseDn, serverId);
- else
- state = new ServerState();
+ new PersistentServerState(baseDn, serverId, new ServerState());
ReplicationBroker broker = new ReplicationBroker(null,
- state, baseDn, serverId, maxRcvQueue, 0,
- maxSendQueue, 0, window_size, 0, generationId,
- getReplSessionSecurity(), (byte)1);
+ state, baseDn.toNormalizedString(), serverId, window_size,
+ generationId, 0, getReplSessionSecurity(), (byte)1);
ArrayList<String> servers = new ArrayList<String>(1);
servers.add("localhost:" + port);
broker.start(servers);
@@ -652,7 +649,7 @@
protected long getMonitorAttrValue(DN baseDn, String attr) throws Exception
{
String monitorFilter =
- "(&(cn=replication plugin*)(base-dn=" + baseDn + "))";
+ "(&(cn=replication Domain*)(base-dn=" + baseDn + "))";
InternalSearchOperation op;
int count = 0;
@@ -1111,5 +1108,5 @@
{
fail("addEntries Exception:"+ e.getMessage() + " " + stackTraceToSingleLineString(e));
}
- }
+ }
}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java
index b388847..e2e27bf 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java
@@ -46,9 +46,8 @@
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyOperationBasis;
import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.replication.service.ReplicationBroker;
import org.opends.server.replication.common.ChangeNumberGenerator;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.protocol.DeleteMsg;
import org.opends.server.replication.protocol.ModifyMsg;
import org.opends.server.replication.protocol.ReplicationMsg;
import org.opends.server.types.Attribute;
@@ -100,7 +99,7 @@
+ "objectClass: ds-cfg-replication-server\n"
+ "cn: Replication Server\n"
+ "ds-cfg-replication-port: " + replServerPort + "\n"
- + "ds-cfg-replication-db-directory: SchemaReplicationTest\n"
+ + "ds-cfg-replication-db-directory: SchemaReplicationTest\n"
+ "ds-cfg-replication-server-id: 105\n";
replServerEntry = TestCaseUtils.entryFromLdifString(replServerLdif);
@@ -247,9 +246,6 @@
* Checks that changes done to the schema files are pushed to the
* ReplicationServers and that the ServerState is updated in the schema
* file.
- * FIXME: This test is disabled because it has side effects.
- * It causes schema tests in org.opends.server.core.AddOperationTestCase
- * to fail when running the build test target.
*/
@Test(enabled=true, dependsOnMethods = { "replaySchemaChange" })
public void pushSchemaFilesChange() throws Exception
@@ -261,7 +257,7 @@
ReplicationBroker broker =
openReplicationSession(baseDn, (short) 3, 100, replServerPort, 5000, true);
-
+
try
{
// create a schema change Notification
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/StressTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/StressTest.java
index a9f7815..6fb9fde 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/StressTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/StressTest.java
@@ -47,7 +47,7 @@
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.replication.plugin.ReplicationBroker;
+import org.opends.server.replication.service.ReplicationBroker;
import org.opends.server.replication.protocol.AddMsg;
import org.opends.server.replication.protocol.ReplicationMsg;
import org.opends.server.types.Attribute;
@@ -203,7 +203,7 @@
+ "objectClass: ds-cfg-replication-server\n"
+ "cn: Replication Server\n"
+ "ds-cfg-replication-port: " + replServerPort + "\n"
- + "ds-cfg-replication-db-directory: StressTest\n"
+ + "ds-cfg-replication-db-directory: StressTest\n"
+ "ds-cfg-replication-server-id: 106\n";
replServerEntry = TestCaseUtils.entryFromLdifString(replServerLdif);
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
index 4bf87f1..175339a 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
@@ -58,8 +58,8 @@
import org.opends.server.protocols.ldap.LDAPModification;
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.common.ChangeNumberGenerator;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.service.ReplicationBroker;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
import org.opends.server.replication.protocol.AddMsg;
import org.opends.server.replication.protocol.DeleteMsg;
import org.opends.server.replication.protocol.HeartbeatThread;
@@ -160,7 +160,7 @@
+ "objectClass: ds-cfg-replication-server\n"
+ "cn: Replication Server\n"
+ "ds-cfg-replication-port: " + replServerPort + "\n"
- + "ds-cfg-replication-db-directory: UpdateOperationTest\n"
+ + "ds-cfg-replication-db-directory: UpdateOperationTest\n"
+ "ds-cfg-replication-server-id: 107\n";
replServerEntry = TestCaseUtils.entryFromLdifString(replServerLdif);
@@ -676,32 +676,32 @@
// because the conflict has been automatically resolved.
assertEquals(DummyAlertHandler.getAlertCount(), AlertCount,
"An alert was incorrectly generated when resolving conflicts");
-
+
/*
- * Test that modify conflict resolution is able to detect that
+ * Test that modify conflict resolution is able to detect that
* because there is a conflict between a MODIFYDN and a MODIFY,
* when a MODIFY is replayed the attribute that is being modified is
* now the RDN of the entry and therefore should not be deleted.
*/
- // send a modify operation attempting to replace the RDN entry
+ // send a modify operation attempting to replace the RDN entry
// with a new value
mods = generatemods("uid", "AnotherUid");
modMsg = new ModifyMsg(gen.newChangeNumber(),
personWithUUIDEntry.getDN(), mods,
user1entryUUID);
-
+
updateMonitorCount(baseDn, resolvedMonitorAttr);
AlertCount = DummyAlertHandler.getAlertCount();
broker.publish(modMsg);
-
+
// check that the modify has been applied.
found = checkEntryHasAttribute(personWithUUIDEntry.getDN(),
"uid", "AnotherUid", 10000, true);
-
+
if (found == false)
fail("The modification has not been correctly replayed.");
assertEquals(getMonitorDelta(), 1);
-
+
/*
* Test that the conflict resolution code is able to detect
* that an entry has been renamed and that a new entry has
@@ -1105,19 +1105,19 @@
assertNotNull(resultEntry,
"The ADD replication message was NOT applied under ou=baseDn2,"+baseDn);
assertEquals(getMonitorDelta(), 1);
-
+
// Check that there was no administrative alert generated
// because the conflict has been automatically resolved.
assertEquals(DummyAlertHandler.getAlertCount(), AlertCount,
"An alert was incorrectly generated when resolving conflicts");
-
+
//
// Check that when a delete is conflicting with Add of some entries
// below the deleted entries, the child entry that have been added
// before the deleted is replayed gets renamed correctly.
//
-
+
// add domain1 entry with 2 children : domain2 and domain3
addEntry(domain1);
domain1uid = getEntryUUID(DN.decode(domain1dn));
@@ -1129,10 +1129,10 @@
"entryUUID = " + domain2uid + "+dc=domain2,ou=people," + TEST_ROOT_DN_STRING);
DN conflictDomain3dn = DN.decode(
"entryUUID = " + domain3uid + "+dc=domain3,ou=people," + TEST_ROOT_DN_STRING);
-
+
updateMonitorCount(baseDn, unresolvedMonitorAttr);
AlertCount = DummyAlertHandler.getAlertCount();
-
+
// delete domain1
delMsg = new DeleteMsg(domain1dn, gen.newChangeNumber(), domain1uid);
broker.publish(delMsg);
@@ -1140,7 +1140,7 @@
// check that the domain1 has correctly been deleted
assertNull(getEntry(DN.decode(domain1dn), 10000, false),
"The DELETE replication message was not replayed");
-
+
// check that domain2 and domain3 have been renamed
assertNotNull(getEntry(conflictDomain2dn, 1000, true),
"The conflicting entries were not created");
@@ -1149,23 +1149,23 @@
// check that the 2 conflicting entries have been correctly marked
assertTrue(checkEntryHasAttribute(conflictDomain2dn,
- ReplicationDomain.DS_SYNC_CONFLICT, domain1dn, 1000, true));
+ LDAPReplicationDomain.DS_SYNC_CONFLICT, domain1dn, 1000, true));
assertTrue(checkEntryHasAttribute(conflictDomain3dn,
- ReplicationDomain.DS_SYNC_CONFLICT, domain1dn, 1000, true));
-
+ LDAPReplicationDomain.DS_SYNC_CONFLICT, domain1dn, 1000, true));
+
// check that unresolved conflict count has been incremented
assertEquals(getMonitorDelta(), 1);
-
+
// Check that an administrative alert was generated
// because the conflict has not been automatically resolved.
assertEquals(DummyAlertHandler.getAlertCount(), AlertCount+2,
"An alert was incorrectly generated when resolving conflicts");
-
+
// delete the resulting entries for the next test
delEntry(conflictDomain2dn);
delEntry(conflictDomain3dn);
-
+
//
// Check that when an entry is added on one master below an entry
// that is currently deleted on another master, the replay of the
@@ -1176,18 +1176,18 @@
domain2.getObjectClassAttribute(),
domain2.getAttributes(), new ArrayList<Attribute>());
broker.publish(addMsg);
-
+
// check that conflict entry was created
assertNotNull(getEntry(conflictDomain2dn, 1000, true),
"The conflicting entries were not created");
-
+
// check that the entry have been correctly marked as conflicting.
assertTrue(checkEntryHasAttribute(conflictDomain2dn,
- ReplicationDomain.DS_SYNC_CONFLICT, domain2dn, 1000, true));
-
+ LDAPReplicationDomain.DS_SYNC_CONFLICT, domain2dn, 1000, true));
+
// check that unresolved conflict count has been incremented
assertEquals(getMonitorDelta(), 1);
-
+
// Check that when an entry is deleted on a first master and
// renamed on a second master and the rename is replayed last
// this is correctly detected as a resolved conflict.
@@ -1215,14 +1215,14 @@
// if the monitor counter did not get incremented after 200sec
// then something got wrong.
assertTrue(count < 200);
-
+
// Check that there was no administrative alert generated
// because the conflict has been automatically resolved.
assertEquals(DummyAlertHandler.getAlertCount(), AlertCount,
"An alert was incorrectly generated when resolving conflicts");
/*
- * Check that a conflict is detected when an entry is
+ * Check that a conflict is detected when an entry is
* moved below an entry that does not exist.
*/
updateMonitorCount(baseDn, unresolvedMonitorAttr);
@@ -1234,7 +1234,7 @@
"uid=wrong, ou=people," + TEST_ROOT_DN_STRING,
"uid=newrdn");
broker.publish(modDnMsg);
-
+
count = 0;
while ((count<2000) && getMonitorDelta() == 0)
{
@@ -1246,11 +1246,11 @@
// if the monitor counter did not get incremented after 200sec
// then something got wrong.
assertTrue(count < 200);
-
+
// check that the entry have been correctly marked as conflicting.
assertTrue(checkEntryHasAttribute(
DN.decode("uid=new person,ou=baseDn2,"+baseDn),
- ReplicationDomain.DS_SYNC_CONFLICT,
+ LDAPReplicationDomain.DS_SYNC_CONFLICT,
"uid=newrdn,ou=baseDn2,ou=People," + TEST_ROOT_DN_STRING, 1000, true));
broker.stop();
@@ -1426,7 +1426,7 @@
if (found == false)
fail("The modification has not been correctly replayed.");
-
+
// Test that replication is able to add attribute that do
// not exist in the schema.
List<Modification> invalidMods = generatemods("badattribute", "value");
@@ -1569,7 +1569,7 @@
"Starting replication test : infiniteReplayLoop"));
final DN baseDn = DN.decode("ou=People," + TEST_ROOT_DN_STRING);
-
+
Thread.sleep(2000);
ReplicationBroker broker =
openReplicationSession(baseDn, (short) 11, 100, replServerPort, 1000, true);
@@ -1709,7 +1709,7 @@
"Starting synchronization test : CNGeneratorAdjust"));
final DN baseDn = DN.decode("ou=People," + TEST_ROOT_DN_STRING);
-
+
/*
* Open a session to the replicationServer using the broker API.
* This must use a different serverId to that of the directory server.
@@ -1731,7 +1731,7 @@
user3UUID,
baseUUID,
user3Entry.getObjectClassAttribute(),
- user3Entry.getAttributes(),
+ user3Entry.getAttributes(),
new ArrayList<Attribute>());
broker.publish(addMsg);
@@ -1745,8 +1745,8 @@
List<Modification> mods = generatemods("telephonenumber", "01 02 45");
ModifyOperationBasis modOp = new ModifyOperationBasis(
connection,
- InternalClientConnection.nextOperationID(),
- InternalClientConnection.nextMessageID(),
+ InternalClientConnection.nextOperationID(),
+ InternalClientConnection.nextMessageID(),
null,
user3Entry.getDN(),
mods);
@@ -1758,10 +1758,10 @@
assertTrue(msg instanceof ModifyMsg,
"The received replication message is not a MODIFY msg");
ModifyMsg modMsg = (ModifyMsg) msg;
- assertEquals(addMsg.getChangeNumber().getTimeSec(),
+ assertEquals(addMsg.getChangeNumber().getTimeSec(),
modMsg.getChangeNumber().getTimeSec(),
"The MOD timestamp should have been adjusted to the ADD one");
-
+
// Delete the entries to clean the database.
DeleteMsg delMsg =
new DeleteMsg(
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/AssuredReplicationPluginTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/AssuredReplicationPluginTest.java
new file mode 100644
index 0000000..d643ab0
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/AssuredReplicationPluginTest.java
@@ -0,0 +1,1535 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2006-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.plugin;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+import org.opends.server.types.ResultCode;
+import org.opends.messages.Category;
+import org.opends.messages.Message;
+import org.opends.messages.Severity;
+import org.opends.server.TestCaseUtils;
+import org.opends.server.core.AddOperationBasis;
+import org.opends.server.core.DeleteOperationBasis;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.common.AssuredMode;
+import org.opends.server.replication.common.DSInfo;
+import org.opends.server.replication.common.RSInfo;
+import org.opends.server.replication.common.ServerState;
+import org.opends.server.replication.common.ServerStatus;
+import org.opends.server.replication.protocol.ProtocolSession;
+import org.opends.server.replication.protocol.ProtocolVersion;
+import org.opends.server.replication.protocol.ReplServerStartMsg;
+import org.opends.server.replication.protocol.ReplSessionSecurity;
+import org.opends.server.replication.protocol.ServerStartMsg;
+import org.opends.server.replication.protocol.StartSessionMsg;
+import org.opends.server.replication.protocol.TopologyMsg;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.protocols.ldap.LDAPFilter;
+import org.opends.server.replication.protocol.AckMsg;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteStringFactory;
+import org.testng.annotations.BeforeClass;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.opends.server.TestCaseUtils.*;
+import static org.testng.Assert.fail;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+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;
+
+/**
+ * Test the client part (plugin) of the assured feature in both safe data and
+ * safe read modes. We use a fake RS and control the behaviour of the client
+ * DS (timeout, wait for acks, error handling...)
+ * Also check for monitoring values for assured replication
+ */
+public class AssuredReplicationPluginTest
+ extends ReplicationTestCase
+{
+
+ /**
+ * The port of the replicationServer.
+ */
+ private int replServerPort;
+ private byte RS_SERVER_ID = (byte) 90;
+ // Sleep time of the RS, before sending an ack
+ private static final long NO_TIMEOUT_RS_SLEEP_TIME = 2000;
+ private String testName = this.getClass().getSimpleName();
+
+ // Create to distincts base dn, one for safe data replication, the other one
+ // for safe read replication
+ private String SAFE_DATA_DN = "ou=safe-data," + TEST_ROOT_DN_STRING;
+ private String SAFE_READ_DN = "ou=safe-read," + TEST_ROOT_DN_STRING;
+ private String NOT_ASSURED_DN = "ou=not-assured," + TEST_ROOT_DN_STRING;
+ private Entry safeDataDomainCfgEntry = null;
+ private Entry safeReadDomainCfgEntry = null;
+ private Entry notAssuredDomainCfgEntry = null;
+
+ // The fake replication server
+ private FakeReplicationServer replicationServer = null;
+
+ // Definitions for the scenario the RS supports
+ private static final int NOT_ASSURED_SCENARIO = 1;
+ private static final int TIMEOUT_SCENARIO = 2;
+ private static final int NO_TIMEOUT_SCENARIO = 3;
+ private static final int SAFE_READ_MANY_ERRORS = 4;
+ private static final int SAFE_DATA_MANY_ERRORS = 5;
+
+ // The tracer object for the debug logger
+ private static final DebugTracer TRACER = getTracer();
+
+ private void debugInfo(String s)
+ {
+ logError(Message.raw(Category.SYNC, Severity.NOTICE, s));
+ if (debugEnabled())
+ {
+ TRACER.debugInfo("** TEST **" + s);
+ }
+ }
+
+ /**
+ * Before starting the tests configure some stuff
+ */
+ @BeforeClass
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ // Find a free port for the replicationServer
+ ServerSocket socket = TestCaseUtils.bindFreePort();
+ replServerPort = socket.getLocalPort();
+ socket.close();
+
+ // Create base dns for each tested modes
+ String topEntry = "dn: " + SAFE_DATA_DN + "\n" + "objectClass: top\n" +
+ "objectClass: organizationalUnit\n";
+ addEntry(TestCaseUtils.entryFromLdifString(topEntry));
+ topEntry = "dn: " + SAFE_READ_DN + "\n" + "objectClass: top\n" +
+ "objectClass: organizationalUnit\n";
+ addEntry(TestCaseUtils.entryFromLdifString(topEntry));
+ topEntry = "dn: " + NOT_ASSURED_DN + "\n" + "objectClass: top\n" +
+ "objectClass: organizationalUnit\n";
+ addEntry(TestCaseUtils.entryFromLdifString(topEntry));
+ }
+
+ /**
+ * Add an entry in the database
+ */
+ private void addEntry(Entry entry) throws Exception
+ {
+ AddOperationBasis addOp = new AddOperationBasis(connection,
+ InternalClientConnection.nextOperationID(), InternalClientConnection.
+ nextMessageID(), null, entry.getDN(), entry.getObjectClasses(),
+ entry.getUserAttributes(), entry.getOperationalAttributes());
+ addOp.setInternalOperation(true);
+ addOp.run();
+ assertNotNull(getEntry(entry.getDN(), 1000, true));
+ }
+
+ /**
+ * Creates a domain using the passed assured settings.
+ * Returns the matching config entry added to the config backend.
+ */
+ private Entry createAssuredDomain(AssuredMode assuredMode, int safeDataLevel,
+ long assuredTimeout) throws Exception
+ {
+ String baseDn = null;
+ switch (assuredMode)
+ {
+ case SAFE_READ_MODE:
+ baseDn = SAFE_READ_DN;
+ break;
+ case SAFE_DATA_MODE:
+ baseDn = SAFE_DATA_DN;
+ break;
+ default:
+ fail("Unexpected assured level.");
+ }
+ // Create an assured config entry ldif, matching passed assured settings
+ String prefixLdif = "dn: cn=" + testName + ", cn=domains, " +
+ SYNCHRO_PLUGIN_DN + "\n" + "objectClass: top\n" +
+ "objectClass: ds-cfg-replication-domain\n" + "cn: " + testName + "\n" +
+ "ds-cfg-base-dn: " + baseDn + "\n" +
+ "ds-cfg-replication-server: localhost:" + replServerPort + "\n" +
+ "ds-cfg-server-id: 1\n" + "ds-cfg-receive-status: true\n" +
+ // heartbeat = 10 min so no need to emulate heartbeat in fake RS: session
+ // not closed by client
+ "ds-cfg-heartbeat-interval: 600000ms\n";
+
+ String configEntryLdif = null;
+ switch (assuredMode)
+ {
+ case SAFE_READ_MODE:
+ configEntryLdif = prefixLdif + "ds-cfg-assured-type: safe-read\n" +
+ "ds-cfg-assured-timeout: " + assuredTimeout + "ms\n";
+ break;
+ case SAFE_DATA_MODE:
+ configEntryLdif = prefixLdif + "ds-cfg-assured-type: safe-data\n" +
+ "ds-cfg-assured-sd-level: " + safeDataLevel + "\n" +
+ "ds-cfg-assured-timeout: " + assuredTimeout + "ms\n";
+ break;
+ default:
+ fail("Unexpected assured level.");
+ }
+ Entry domainCfgEntry = TestCaseUtils.entryFromLdifString(configEntryLdif);
+
+ // Add the config entry to create the replicated domain
+ DirectoryServer.getConfigHandler().addEntry(domainCfgEntry, null);
+ assertNotNull(DirectoryServer.getConfigEntry(domainCfgEntry.getDN()),
+ "Unable to add the domain config entry: " + configEntryLdif);
+
+ return domainCfgEntry;
+ }
+
+ /**
+ * Creates a domain without assured mode
+ * Returns the matching config entry added to the config backend.
+ */
+ private Entry createNotAssuredDomain() throws Exception
+ {
+
+ // Create a not assured config entry ldif
+ String configEntryLdif = "dn: cn=" + testName + ", cn=domains, " +
+ SYNCHRO_PLUGIN_DN + "\n" + "objectClass: top\n" +
+ "objectClass: ds-cfg-replication-domain\n" + "cn: " + testName + "\n" +
+ "ds-cfg-base-dn: " + NOT_ASSURED_DN + "\n" +
+ "ds-cfg-replication-server: localhost:" + replServerPort + "\n" +
+ "ds-cfg-server-id: 1\n" + "ds-cfg-receive-status: true\n" +
+ // heartbeat = 10 min so no need to emulate heartbeat in fake RS: session
+ // not closed by client
+ "ds-cfg-heartbeat-interval: 600000ms\n";
+
+ Entry domainCfgEntry = TestCaseUtils.entryFromLdifString(configEntryLdif);
+
+ // Add the config entry to create the replicated domain
+ DirectoryServer.getConfigHandler().addEntry(domainCfgEntry, null);
+ assertNotNull(DirectoryServer.getConfigEntry(domainCfgEntry.getDN()),
+ "Unable to add the domain config entry: " + configEntryLdif);
+
+ return domainCfgEntry;
+ }
+
+ /**
+ * Removes a domain deleting the passed config entry
+ */
+ private void removeDomain(Entry domainCfgEntry)
+ {
+ DeleteOperationBasis op;
+ // Delete entries
+ try
+ {
+ DN dn = domainCfgEntry.getDN();
+
+ logError(Message.raw(Category.SYNC, Severity.NOTICE,
+ "cleaning config entry " + dn));
+
+ op = new DeleteOperationBasis(connection, InternalClientConnection.
+ nextOperationID(), InternalClientConnection.nextMessageID(), null,
+ dn);
+ op.run();
+ if ((op.getResultCode() != ResultCode.SUCCESS) &&
+ (op.getResultCode() != ResultCode.NO_SUCH_OBJECT))
+ {
+ fail("Deleting config entry" + dn +
+ " failed: " + op.getResultCode().getResultCodeName());
+ }
+ } catch (NoSuchElementException e)
+ {
+ // done
+ }
+ }
+
+ /**
+ * The fake replication server used to emulate RS behaviour the way we want
+ * for assured features test
+ */
+ private class FakeReplicationServer extends Thread
+ {
+
+ private ServerSocket listenSocket;
+ private boolean shutdown = false;
+ private ProtocolSession session = null;
+
+ // Parameters given at constructor time
+ private int port;
+ private short serverId = -1;
+ boolean isAssured = false; // Default value for config
+ AssuredMode assuredMode = AssuredMode.SAFE_DATA_MODE; // Default value for config
+ byte safeDataLevel = (byte) 1; // Default value for config
+
+ // Predefined config parameters
+ private int degradedStatusThreshold = 5000;
+
+ // Parameters set with received server start message
+ private String baseDn = null;
+ private long generationId = -1L;
+ private ServerState serverState = null;
+ private int windowSize = -1;
+ private byte groupId = (byte) -1;
+ private boolean sslEncryption = false;
+ // The scenario this RS is expecting
+ private int scenario = -1;
+
+ // parameters at handshake are ok
+ private boolean handshakeOk = false;
+ // signal that the current scenario the RS must execute reached the point
+ // where the main code can perform test assertion
+ private boolean scenarioExecuted = false;
+
+ // Constructor for RS receiving updates in SR assured mode or not assured
+ // The assured boolean means:
+ // - true: SR mode
+ // - false: not assured
+ public FakeReplicationServer(int port, short serverId, boolean assured)
+ {
+
+ this.port = port;
+ this.serverId = serverId;
+
+ if (assured)
+ {
+ this.isAssured = true;
+ this.assuredMode = AssuredMode.SAFE_READ_MODE;
+ }
+ }
+
+ // Constructor for RS receiving updates in SD assured mode
+ public FakeReplicationServer(int port, short serverId, int safeDataLevel)
+ {
+
+ this.port = port;
+ this.serverId = serverId;
+
+ this.isAssured = true;
+ this.assuredMode = AssuredMode.SAFE_DATA_MODE;
+ this.safeDataLevel = (byte) safeDataLevel;
+ }
+
+ /**
+ * Starts the fake RS, expecting and testing the passed scenario.
+ */
+ public void start(int scenario)
+ {
+
+ // Store expected test case
+ this.scenario = scenario;
+
+ // Start listening
+ start();
+ }
+
+ /**
+ * Wait for DS connections
+ */
+ @Override
+ public void run()
+ {
+
+ // Create server socket
+ try
+ {
+ listenSocket = new ServerSocket();
+ listenSocket.bind(new InetSocketAddress(port));
+ } catch (IOException e)
+ {
+ fail("Fake replication server could not bind to port:" + port);
+ }
+
+ Socket newSocket = null;
+ // Loop waiting for DS connections
+ while (!shutdown)
+ {
+ try
+ {
+ newSocket = listenSocket.accept();
+ newSocket.setTcpNoDelay(true);
+ newSocket.setKeepAlive(true);
+ // Create client session
+ ReplSessionSecurity replSessionSecurity = new ReplSessionSecurity();
+ session = replSessionSecurity.createServerSession(newSocket,
+ ReplSessionSecurity.HANDSHAKE_TIMEOUT);
+ if (session == null) // Error, go back to accept
+ {
+ continue;
+ }
+ // Ok, now call connection handling code + special code for the
+ // configured test
+ handleClientConnection();
+ } catch (Exception e)
+ {
+ // The socket has probably been closed as part of the
+ // shutdown
+ }
+ }
+ }
+
+ /**
+ * Shutdown the Replication Server service and all its connections.
+ */
+ public void shutdown()
+ {
+ if (shutdown)
+ {
+ return;
+ }
+
+ shutdown = true;
+
+ // Shutdown the listener thread
+ try
+ {
+ if (listenSocket != null)
+ {
+ listenSocket.close();
+ }
+ } catch (IOException e)
+ {
+ // replication Server service is closing anyway.
+ }
+
+ /*
+ * Shutdown any current client handling code
+ */
+ try
+ {
+ if (session != null)
+ {
+ session.close();
+ }
+ } catch (IOException e)
+ {
+ // ignore.
+ }
+
+ try
+ {
+ join();
+ } catch (InterruptedException ie)
+ {
+ }
+ }
+
+ /**
+ * Handle the handshake processing with the connecting DS
+ * returns true if handshake was performed without errors
+ */
+ private boolean performHandshake()
+ {
+
+ try
+ {
+ // Receive server start
+ ServerStartMsg serverStartMsg = (ServerStartMsg) session.receive();
+
+ baseDn = serverStartMsg.getBaseDn();
+ serverState = serverStartMsg.getServerState();
+ generationId = serverStartMsg.getGenerationId();
+ windowSize = serverStartMsg.getWindowSize();
+ groupId = serverStartMsg.getGroupId();
+ sslEncryption = serverStartMsg.getSSLEncryption();
+
+ // Send replication server start
+ String serverURL = ("localhost:" + port);
+ ReplServerStartMsg replServerStartMsg = new ReplServerStartMsg(serverId,
+ serverURL, baseDn, windowSize, serverState,
+ ProtocolVersion.getCurrentVersion(), generationId, sslEncryption,
+ groupId, degradedStatusThreshold);
+ session.publish(replServerStartMsg);
+
+ if (!sslEncryption)
+ {
+ session.stopEncryption();
+ }
+
+ // Read start session
+ StartSessionMsg startSessionMsg = (StartSessionMsg) session.receive();
+
+ // Sanity checking for assured parameters
+ boolean receivedIsAssured = startSessionMsg.isAssured();
+ assertEquals(receivedIsAssured, isAssured);
+ AssuredMode receivedAssuredMode = startSessionMsg.getAssuredMode();
+ assertEquals(receivedAssuredMode, assuredMode);
+ byte receivedSafeDataLevel = startSessionMsg.getSafeDataLevel();
+ assertEquals(receivedSafeDataLevel, safeDataLevel);
+ ServerStatus receivedServerStatus = startSessionMsg.getStatus();
+ assertEquals(receivedServerStatus, ServerStatus.NORMAL_STATUS);
+ List<String> receivedReferralsURLs = startSessionMsg.getReferralsURLs();
+ assertEquals(receivedReferralsURLs.size(), 0);
+
+ debugInfo("Received start session assured parameters are ok.");
+
+ // Send topo view
+ List<RSInfo> rsList = new ArrayList<RSInfo>();
+ RSInfo rsInfo = new RSInfo(serverId, generationId, groupId);
+ rsList.add(rsInfo);
+ TopologyMsg topologyMsg = new TopologyMsg(new ArrayList<DSInfo>(),
+ rsList);
+ session.publish(topologyMsg);
+
+ } catch (IOException e)
+ {
+ // Probably un-connection of DS looking for best server
+ return false;
+ } catch (Exception e)
+ {
+ fail("Unexpected exception in fake replication server handshake " +
+ "processing: " + e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Tells if the received handshake parameters regarding assured config were
+ * ok and handshake phase is terminated.
+ */
+ public boolean isHandshakeOk()
+ {
+
+ return handshakeOk;
+ }
+
+ /**
+ * Tells the main code that the fake RS executed enough of the expected
+ * scenario and can perform test assertion
+ */
+ public boolean isScenarioExecuted()
+ {
+
+ return scenarioExecuted;
+ }
+
+ /**
+ * Handle client connection then call code specific to configured test
+ */
+ private void handleClientConnection()
+ {
+
+ // Handle DS connexion
+ if (!performHandshake())
+ {
+ try
+ {
+ session.close();
+ } catch (IOException e)
+ {
+ // nothing to do
+ }
+ return;
+ }
+ // If we come here, assured parameters sent by DS are as expected and
+ // handshake phase is terminated
+ handshakeOk = true;
+
+ // Now execute the requested scenario
+ switch (scenario)
+ {
+ case NOT_ASSURED_SCENARIO:
+ executeNotAssuredScenario();
+ break;
+ case TIMEOUT_SCENARIO:
+ executeTimeoutScenario();
+ break;
+ case NO_TIMEOUT_SCENARIO:
+ executeNoTimeoutScenario();
+ break;
+ case SAFE_READ_MANY_ERRORS:
+ executeSafeReadManyErrorsScenario();
+ break;
+ case SAFE_DATA_MANY_ERRORS:
+ executeSafeDataManyErrorsScenario();
+ break;
+ default:
+ fail("Unknown scenario: " + scenario);
+ }
+ }
+
+ /**
+ * Read the coming update and check parameters are not assured
+ */
+ private void executeNotAssuredScenario()
+ {
+
+ try
+ {
+ UpdateMsg updateMsg = (UpdateMsg) session.receive();
+ checkUpdateAssuredParameters(updateMsg);
+
+ scenarioExecuted = true;
+ } catch (Exception e)
+ {
+ fail("Unexpected exception in fake replication server executeNotAssuredScenario " +
+ "processing: " + e);
+ }
+ }
+
+ /**
+ * Read the coming update and make the client time out by not sending back
+ * the ack
+ */
+ private void executeTimeoutScenario()
+ {
+
+ try
+ {
+ UpdateMsg updateMsg = (UpdateMsg) session.receive();
+ checkUpdateAssuredParameters(updateMsg);
+
+ scenarioExecuted = true;
+
+ // We do not send back an ack and the client code is expected to be
+ // blocked at least for the programmed timeout time.
+
+ } catch (Exception e)
+ {
+ fail("Unexpected exception in fake replication server executeTimeoutScenario " +
+ "processing: " + e);
+ }
+ }
+
+ /**
+ * Read the coming update, sleep some time then send back an ack
+ */
+ private void executeNoTimeoutScenario()
+ {
+
+ try
+ {
+ UpdateMsg updateMsg = (UpdateMsg) session.receive();
+ checkUpdateAssuredParameters(updateMsg);
+
+ // Sleep before sending back the ack
+ sleep(NO_TIMEOUT_RS_SLEEP_TIME);
+
+ // Send the ack without errors
+ AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber());
+ session.publish(ackMsg);
+
+ scenarioExecuted = true;
+
+ } catch (Exception e)
+ {
+ fail("Unexpected exception in fake replication server executeNoTimeoutScenario " +
+ "processing: " + e);
+ }
+ }
+
+ /**
+ * Check that received update assured parameters are as defined at RS start
+ */
+ private void checkUpdateAssuredParameters(UpdateMsg updateMsg)
+ {
+ assertEquals(updateMsg.isAssured(), isAssured);
+ assertEquals(updateMsg.getAssuredMode(), assuredMode);
+ assertEquals(updateMsg.getSafeDataLevel(), safeDataLevel);
+ debugInfo("Received update assured parameters are ok.");
+ }
+
+ /**
+ * Read the coming seaf read mode updates and send back acks with errors
+ */
+ private void executeSafeReadManyErrorsScenario()
+ {
+
+ try
+ {
+ // Read first update
+ UpdateMsg updateMsg = (UpdateMsg) session.receive();
+ checkUpdateAssuredParameters(updateMsg);
+
+ // Sleep before sending back the ack
+ sleep(NO_TIMEOUT_RS_SLEEP_TIME);
+
+ // Send an ack with errors:
+ // - replay error
+ // - server 10 error, server 20 error
+ List<Short> serversInError = new ArrayList<Short>();
+ serversInError.add((short)10);
+ serversInError.add((short)20);
+ AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber(), false, false, true, serversInError);
+ session.publish(ackMsg);
+
+ // Read second update
+ updateMsg = (UpdateMsg) session.receive();
+ checkUpdateAssuredParameters(updateMsg);
+
+ // Sleep before sending back the ack
+ sleep(NO_TIMEOUT_RS_SLEEP_TIME);
+
+ // Send an ack with errors:
+ // - timeout error
+ // - wrong status error
+ // - replay error
+ // - server 10 error, server 20 error, server 30 error
+ serversInError = new ArrayList<Short>();
+ serversInError.add((short)10);
+ serversInError.add((short)20);
+ serversInError.add((short)30);
+ ackMsg = new AckMsg(updateMsg.getChangeNumber(), true, true, true, serversInError);
+ session.publish(ackMsg);
+
+ // Read third update
+ updateMsg = (UpdateMsg) session.receive();
+ checkUpdateAssuredParameters(updateMsg);
+
+ // let timeout occur
+
+ scenarioExecuted = true;
+
+ } catch (Exception e)
+ {
+ fail("Unexpected exception in fake replication server executeSafeReadManyErrorsScenario " +
+ "processing: " + e);
+ }
+ }
+
+ /**
+ * Read the coming seaf data mode updates and send back acks with errors
+ */
+ private void executeSafeDataManyErrorsScenario()
+ {
+
+ try
+ {
+ // Read first update
+ UpdateMsg updateMsg = (UpdateMsg) session.receive();
+ checkUpdateAssuredParameters(updateMsg);
+
+ // Sleep before sending back the ack
+ sleep(NO_TIMEOUT_RS_SLEEP_TIME);
+
+ // Send an ack with errors:
+ // - timeout error
+ // - server 10 error
+ List<Short> serversInError = new ArrayList<Short>();
+ serversInError.add((short)10);
+ AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber(), true, false, false, serversInError);
+ session.publish(ackMsg);
+
+ // Read second update
+ updateMsg = (UpdateMsg) session.receive();
+ checkUpdateAssuredParameters(updateMsg);
+
+ // Sleep before sending back the ack
+ sleep(NO_TIMEOUT_RS_SLEEP_TIME);
+
+ // Send an ack with errors:
+ // - timeout error
+ // - server 10 error, server 20 error
+ serversInError = new ArrayList<Short>();
+ serversInError.add((short)10);
+ serversInError.add((short)20);
+ ackMsg = new AckMsg(updateMsg.getChangeNumber(), true, false, false, serversInError);
+ session.publish(ackMsg);
+
+ // Read third update
+ updateMsg = (UpdateMsg) session.receive();
+ checkUpdateAssuredParameters(updateMsg);
+
+ // let timeout occur
+
+ scenarioExecuted = true;
+
+ } catch (Exception e)
+ {
+ fail("Unexpected exception in fake replication server executeSafeDataManyErrorsScenario " +
+ "processing: " + e);
+ }
+ }
+ }
+
+ /**
+ * Sleep a while
+ */
+ private void sleep(long time)
+ {
+ try
+ {
+ Thread.sleep(time);
+ } catch (InterruptedException ex)
+ {
+ fail("Error sleeping " + ex);
+ }
+ }
+
+ /**
+ * Tests that a DS performing a modification in safe data mode waits for
+ * the ack of the RS for the configured timeout time, then times out.
+ */
+ @Test
+ public void testSafeDataModeTimeout() throws Exception
+ {
+
+ int TIMEOUT = 5000;
+ String testcase = "testSafeDataModeTimeout";
+ try
+ {
+ // Create and start a RS expecting clients in safe data assured mode with
+ // safe data level 2
+ replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+ 1);
+ replicationServer.start(TIMEOUT_SCENARIO);
+
+ // Create a safe data assured domain
+ safeDataDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_DATA_MODE, 1,
+ TIMEOUT);
+ // Wait for connection of domain to RS
+ waitForConnectionToRs(testcase, replicationServer);
+
+ // Make an LDAP update (add an entry)
+ long startTime = System.currentTimeMillis(); // Time the update has been initiated
+ String entry = "dn: ou=assured-sd-timeout-entry," + SAFE_DATA_DN + "\n" +
+ "objectClass: top\n" +
+ "objectClass: organizationalUnit\n";
+ addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+ // In this scenario, the fake RS will not send back an ack so we expect
+ // the add entry code (LDAP client code emulation) to be blocked for the
+ // timeout value at least. If the time we have slept is lower, timeout
+ // handling code is not working...
+ long endTime = System.currentTimeMillis();
+ assertTrue((endTime - startTime) >= TIMEOUT);
+ assertTrue(replicationServer.isScenarioExecuted());
+
+ // Check monitoring values
+ DN baseDn = DN.decode(SAFE_DATA_DN);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+ Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+ assertTrue(errorsByServer.isEmpty());
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 1);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+ // errors by server list for sd mode should be [[rsId:1]]
+ assertEquals(errorsByServer.size(), 1);
+ Integer nError = errorsByServer.get((short)RS_SERVER_ID);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 1);
+
+ } finally
+ {
+ endTest();
+ }
+ }
+
+ /**
+ * Tests that a DS performing a modification in safe read mode waits for
+ * the ack of the RS for the configured timeout time, then times out.
+ */
+ @Test
+ public void testSafeReadModeTimeout() throws Exception
+ {
+
+ int TIMEOUT = 5000;
+ String testcase = "testSafeReadModeTimeout";
+ try
+ {
+ // Create and start a RS expecting clients in safe read assured mode
+ replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+ true);
+ replicationServer.start(TIMEOUT_SCENARIO);
+
+ // Create a safe read assured domain
+ safeReadDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_READ_MODE, 0,
+ TIMEOUT);
+ // Wait for connection of domain to RS
+ waitForConnectionToRs(testcase, replicationServer);
+
+ // Make an LDAP update (add an entry)
+ long startTime = System.currentTimeMillis(); // Time the update has been initiated
+ String entry = "dn: ou=assured-sr-timeout-entry," + SAFE_READ_DN + "\n" +
+ "objectClass: top\n" +
+ "objectClass: organizationalUnit\n";
+ addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+ // In this scenario, the fake RS will not send back an ack so we expect
+ // the add entry code (LDAP client code emulation) to be blocked for the
+ // timeout value at least. If the time we have slept is lower, timeout
+ // handling code is not working...
+ long endTime = System.currentTimeMillis();
+ assertTrue((endTime - startTime) >= TIMEOUT);
+ assertTrue(replicationServer.isScenarioExecuted());
+
+ // Check monitoring values
+ DN baseDn = DN.decode(SAFE_READ_DN);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+ Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+ // errors by server list for sr mode should be [[rsId:1]]
+ assertEquals(errorsByServer.size(), 1);
+ Integer nError = errorsByServer.get((short)RS_SERVER_ID);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+ assertTrue(errorsByServer.isEmpty());
+
+ } finally
+ {
+ endTest();
+ }
+ }
+
+ /**
+ * Tests parameters sent in session handshake an updates, when not using
+ * assured replication
+ */
+ @Test
+ public void testNotAssuredSession() throws Exception
+ {
+
+ String testcase = "testNotAssuredSession";
+ try
+ {
+ // Create and start a RS expecting not assured clients
+ replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+ false);
+ replicationServer.start(NOT_ASSURED_SCENARIO);
+
+ // Create a not assured domain
+ notAssuredDomainCfgEntry = createNotAssuredDomain();
+ // Wait for connection of domain to RS
+ waitForConnectionToRs(testcase, replicationServer);
+
+ // Make an LDAP update (add an entry)
+ String entry = "dn: ou=not-assured-entry," + NOT_ASSURED_DN + "\n" +
+ "objectClass: top\n" +
+ "objectClass: organizationalUnit\n";
+ addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+ // Wait for entry received by RS
+ waitForScenarioExecutedOnRs(testcase, replicationServer);
+
+ // No more test to do here
+ } finally
+ {
+ endTest();
+ }
+ }
+
+ /**
+ * Wait for connection to the fake replication server or times out with error
+ * after some seconds
+ */
+ private void waitForConnectionToRs(String testCase, FakeReplicationServer rs)
+ {
+ int nsec = -1;
+ do
+ {
+ nsec++;
+ if (nsec == 10) // 10 seconds timeout
+ fail(testCase + ": timeout waiting for domain connection to fake RS after " + nsec + " seconds.");
+ sleep(1000);
+ } while (!rs.isHandshakeOk());
+ }
+
+ /**
+ * Wait for the scenario to be executed by the fake replication server or
+ * times out with error after some seconds
+ */
+ private void waitForScenarioExecutedOnRs(String testCase, FakeReplicationServer rs)
+ {
+ int nsec = -1;
+ do
+ {
+ nsec++;
+ if (nsec == 10) // 10 seconds timeout
+ fail(testCase + ": timeout waiting for scenario to be exectued on fake RS after " + nsec + " seconds.");
+ sleep(1000);
+ } while (!rs.isScenarioExecuted());
+ }
+
+ private void endTest()
+ {
+ if (replicationServer != null)
+ {
+ replicationServer.shutdown();
+ }
+
+ if (safeDataDomainCfgEntry != null)
+ {
+ removeDomain(safeDataDomainCfgEntry);
+ }
+
+ if (safeReadDomainCfgEntry != null)
+ {
+ removeDomain(safeReadDomainCfgEntry);
+ }
+
+ if (notAssuredDomainCfgEntry != null)
+ {
+ removeDomain(notAssuredDomainCfgEntry);
+ }
+ }
+
+ /**
+ * Tests that a DS performing a modification in safe data mode receives the RS
+ * ack and does not return before returning it.
+ */
+ @Test
+ public void testSafeDataModeAck() throws Exception
+ {
+
+ int TIMEOUT = 5000;
+ String testcase = "testSafeDataModeAck";
+ try
+ {
+ // Create and start a RS expecting clients in safe data assured mode with
+ // safe data level 2
+ replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+ 2);
+ replicationServer.start(NO_TIMEOUT_SCENARIO);
+
+ // Create a safe data assured domain
+ safeDataDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_DATA_MODE, 2,
+ TIMEOUT);
+ // Wait for connection of domain to RS
+ waitForConnectionToRs(testcase, replicationServer);
+
+ // Make an LDAP update (add an entry)
+ long startTime = System.currentTimeMillis(); // Time the update has been initiated
+ String entry = "dn: ou=assured-sd-no-timeout-entry," + SAFE_DATA_DN + "\n" +
+ "objectClass: top\n" +
+ "objectClass: organizationalUnit\n";
+ addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+ // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+ // seconds, so we expect the add entry code (LDAP client code emulation) to be blocked
+ // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+ long endTime = System.currentTimeMillis();
+ long callTime = endTime - startTime;
+ assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+ assertTrue(replicationServer.isScenarioExecuted());
+
+ // Check monitoring values
+ DN baseDn = DN.decode(SAFE_DATA_DN);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+ Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+ assertTrue(errorsByServer.isEmpty());
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+ assertTrue(errorsByServer.isEmpty());
+
+ } finally
+ {
+ endTest();
+ }
+ }
+
+ /**
+ * Tests that a DS performing a modification in safe read mode receives the RS
+ * ack and does not return before returning it.
+ */
+ @Test
+ public void testSafeReadModeAck() throws Exception
+ {
+
+ int TIMEOUT = 5000;
+ String testcase = "testSafeReadModeAck";
+ try
+ {
+ // Create and start a RS expecting clients in safe read assured mode
+ replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+ true);
+ replicationServer.start(NO_TIMEOUT_SCENARIO);
+
+ // Create a safe read assured domain
+ safeReadDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_READ_MODE, 0,
+ TIMEOUT);
+ // Wait for connection of domain to RS
+ waitForConnectionToRs(testcase, replicationServer);
+
+ // Make an LDAP update (add an entry)
+ long startTime = System.currentTimeMillis(); // Time the update has been initiated
+ String entry = "dn: ou=assured-sr-no-timeout-entry," + SAFE_READ_DN + "\n" +
+ "objectClass: top\n" +
+ "objectClass: organizationalUnit\n";
+ addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+ // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+ // seconds, so we expect the add entry code (LDAP client code emulation) to be blocked
+ // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+ long endTime = System.currentTimeMillis();
+ long callTime = endTime - startTime;
+ assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+ assertTrue(replicationServer.isScenarioExecuted());
+
+ // Check monitoring values
+ DN baseDn = DN.decode(SAFE_READ_DN);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+ Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+ assertTrue(errorsByServer.isEmpty());
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+ assertTrue(errorsByServer.isEmpty());
+
+ } finally
+ {
+ endTest();
+ }
+ }
+
+ /**
+ * DS performs many successive modifications in safe data mode and receives RS
+ * acks with various errors. Check for monitoring right errors
+ */
+ @Test
+ public void testSafeDataManyErrors() throws Exception
+ {
+
+ int TIMEOUT = 5000;
+ String testcase = "testSafeDataManyErrors";
+ try
+ {
+ // Create and start a RS expecting clients in safe data assured mode with
+ // safe data level 3
+ replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+ 3);
+ replicationServer.start(SAFE_DATA_MANY_ERRORS);
+
+ // Create a safe data assured domain
+ safeDataDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_DATA_MODE, 3,
+ TIMEOUT);
+ // Wait for connection of domain to RS
+ waitForConnectionToRs(testcase, replicationServer);
+
+ // Make a first LDAP update (add an entry)
+ long startTime = System.currentTimeMillis(); // Time the update has been initiated
+ String entryDn = "ou=assured-sd-many-errors-entry," + SAFE_DATA_DN;
+ String entry = "dn: " + entryDn + "\n" +
+ "objectClass: top\n" +
+ "objectClass: organizationalUnit\n";
+ addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+ // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+ // seconds, so we expect the add entry code (LDAP client code emulation) to be blocked
+ // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+ long endTime = System.currentTimeMillis();
+ long callTime = endTime - startTime;
+ assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+
+ // Check monitoring values
+ // The expected ack for the first update is:
+ // - timeout error
+ // - server 10 error
+ DN baseDn = DN.decode(SAFE_DATA_DN);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+ Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+ assertTrue(errorsByServer.isEmpty());
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 1);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+ // errors by server list for sd mode should be [[10:1]]
+ assertEquals(errorsByServer.size(), 1);
+ Integer nError = errorsByServer.get((short)10);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 1);
+
+ // Make a second LDAP update (delete the entry)
+ startTime = System.currentTimeMillis(); // Time the update has been initiated
+ deleteEntry(entryDn);
+
+ // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+ // seconds, so we expect the delete entry code (LDAP client code emulation) to be blocked
+ // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+ endTime = System.currentTimeMillis();
+ callTime = endTime - startTime;
+ assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+
+ // Check monitoring values
+ // The expected ack for the second update is:
+ // - timeout error
+ // - server 10 error, server 20 error
+ baseDn = DN.decode(SAFE_DATA_DN);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+ assertTrue(errorsByServer.isEmpty());
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 2);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 2);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+ // errors by server list for sd mode should be [[10:2],[20:1]]
+ assertEquals(errorsByServer.size(), 2);
+ nError = errorsByServer.get((short)10);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 2);
+ nError = errorsByServer.get((short)20);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 1);
+
+ // Make a third LDAP update (re-add the entry)
+ startTime = System.currentTimeMillis(); // Time the update has been initiated
+ addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+ // In this scenario, the fake RS will not send back an ack so we expect
+ // the add entry code (LDAP client code emulation) to be blocked for the
+ // timeout value at least. If the time we have slept is lower, timeout
+ // handling code is not working...
+ endTime = System.currentTimeMillis();
+ assertTrue((endTime - startTime) >= TIMEOUT);
+ assertTrue(replicationServer.isScenarioExecuted());
+
+ // Check monitoring values
+ // No ack should have comen back, so timeout incremented (flag and error for rs)
+ baseDn = DN.decode(SAFE_DATA_DN);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 0);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+ assertTrue(errorsByServer.isEmpty());
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 3);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 3);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+ // errors by server list for sd mode should be [[10:2],[20:1],[rsId:1]]
+ assertEquals(errorsByServer.size(), 3);
+ nError = errorsByServer.get((short)10);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 2);
+ nError = errorsByServer.get((short)20);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 1);
+ nError = errorsByServer.get((short)RS_SERVER_ID);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 1);
+
+ } finally
+ {
+ endTest();
+ }
+ }
+
+ /**
+ * DS performs many successive modifications in safe read mode and receives RS
+ * acks with various errors. Check for monitoring right errors
+ */
+ @Test
+ public void testSafeReadManyErrors() throws Exception
+ {
+
+ int TIMEOUT = 5000;
+ String testcase = "testSafeReadManyErrors";
+ try
+ {
+ // Create and start a RS expecting clients in safe read assured mode
+ replicationServer = new FakeReplicationServer(replServerPort, RS_SERVER_ID,
+ true);
+ replicationServer.start(SAFE_READ_MANY_ERRORS);
+
+ // Create a safe read assured domain
+ safeReadDomainCfgEntry = createAssuredDomain(AssuredMode.SAFE_READ_MODE, 0,
+ TIMEOUT);
+ // Wait for connection of domain to RS
+ waitForConnectionToRs(testcase, replicationServer);
+
+ // Make a first LDAP update (add an entry)
+ long startTime = System.currentTimeMillis(); // Time the update has been initiated
+ String entryDn = "ou=assured-sr-many-errors-entry," + SAFE_READ_DN;
+ String entry = "dn: " + entryDn + "\n" +
+ "objectClass: top\n" +
+ "objectClass: organizationalUnit\n";
+ addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+ // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+ // seconds, so we expect the add entry code (LDAP client code emulation) to be blocked
+ // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+ long endTime = System.currentTimeMillis();
+ long callTime = endTime - startTime;
+ assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+
+ // Check monitoring values
+ // The expected ack for the first update is:
+ // - replay error
+ // - server 10 error, server 20 error
+ DN baseDn = DN.decode(SAFE_READ_DN);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 1);
+ Map<Short, Integer> errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+ // errors by server list for sr mode should be [[10:1],[20:1]]
+ assertEquals(errorsByServer.size(), 2);
+ Integer nError = errorsByServer.get((short)10);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 1);
+ nError = errorsByServer.get((short)20);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+ assertTrue(errorsByServer.isEmpty());
+
+ // Make a second LDAP update (delete the entry)
+ startTime = System.currentTimeMillis(); // Time the update has been initiated
+ deleteEntry(entryDn);
+
+ // In this scenario, the fake RS will send back an ack after NO_TIMEOUT_RS_SLEEP_TIME
+ // seconds, so we expect the delete entry code (LDAP client code emulation) to be blocked
+ // for more than NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
+ endTime = System.currentTimeMillis();
+ callTime = endTime - startTime;
+ assertTrue( (callTime >= NO_TIMEOUT_RS_SLEEP_TIME) && (callTime <= TIMEOUT));
+
+ // Check monitoring values
+ // The expected ack for the second update is:
+ // - timeout error
+ // - wrong status error
+ // - replay error
+ // - server 10 error, server 20 error, server 30 error
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 2);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 2);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 2);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+ // errors by server list for sr mode should be [[10:2],[20:2],[30:1]]
+ assertEquals(errorsByServer.size(), 3);
+ nError = errorsByServer.get((short)10);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 2);
+ nError = errorsByServer.get((short)20);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 2);
+ nError = errorsByServer.get((short)30);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+ assertTrue(errorsByServer.isEmpty());
+
+ // Make a third LDAP update (re-add the entry)
+ startTime = System.currentTimeMillis(); // Time the update has been initiated
+ addEntry(TestCaseUtils.entryFromLdifString(entry));
+
+ // In this scenario, the fake RS will not send back an ack so we expect
+ // the add entry code (LDAP client code emulation) to be blocked for the
+ // timeout value at least. If the time we have slept is lower, timeout
+ // handling code is not working...
+ endTime = System.currentTimeMillis();
+ assertTrue((endTime - startTime) >= TIMEOUT);
+ assertTrue(replicationServer.isScenarioExecuted());
+
+ // Check monitoring values
+ // No ack should have comen back, so timeout incremented (flag and error for rs)
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-sent-updates"), 3);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-not-acknowledged-updates"), 3);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-timeout-updates"), 2);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-wrong-status-updates"), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sr-replay-error-updates"), 2);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_READ_MODE);
+ // errors by server list for sr mode should be [[10:2],[20:2],[30:1],[rsId:1]]
+ assertEquals(errorsByServer.size(), 4);
+ nError = errorsByServer.get((short)10);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 2);
+ nError = errorsByServer.get((short)20);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 2);
+ nError = errorsByServer.get((short)30);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 1);
+ nError = errorsByServer.get((short)RS_SERVER_ID);
+ assertNotNull(nError);
+ assertEquals(nError.intValue(), 1);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-sent-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-acknowledged-updates"), 0);
+ assertEquals(getMonitorAttrValue(baseDn, "assured-sd-timeout-updates"), 0);
+ errorsByServer = getErrorsByServers(baseDn, AssuredMode.SAFE_DATA_MODE);
+ assertTrue(errorsByServer.isEmpty());
+
+ } finally
+ {
+ endTest();
+ }
+ }
+
+ /**
+ * Delete an entry from the database
+ */
+ private void deleteEntry(String dn) throws Exception
+ {
+ DN realDn = DN.decode(dn);
+ DeleteOperationBasis delOp = new DeleteOperationBasis(connection,
+ InternalClientConnection.nextOperationID(), InternalClientConnection.
+ nextMessageID(), null, realDn);
+ delOp.setInternalOperation(true);
+ delOp.run();
+ assertNull(DirectoryServer.getEntry(realDn));
+ }
+
+ /**
+ * Get the errors by servers from cn=monitor, according to the requested base dn
+ * and the requested mode
+ * This corresponds to the values for multi valued attributes:
+ * - assured-sr-server-not-acknowledged-updates in SR mode
+ * - assured-sd-server-timeout-updates in SD mode
+ */
+ protected Map<Short,Integer> getErrorsByServers(DN baseDn,
+ AssuredMode assuredMode) throws Exception
+ {
+ /*
+ * Find monitoring entry for requested base DN
+ */
+ String monitorFilter =
+ "(&(cn=replication Domain*)(base-dn=" + baseDn + "))";
+
+ InternalSearchOperation op;
+ int count = 0;
+ do
+ {
+ if (count++>0)
+ Thread.sleep(100);
+ op = connection.processSearch(
+ ByteStringFactory.create("cn=monitor"),
+ SearchScope.SINGLE_LEVEL,
+ LDAPFilter.decode(monitorFilter));
+ }
+ while (op.getSearchEntries().isEmpty() && (count<100));
+ if (op.getSearchEntries().isEmpty())
+ throw new Exception("Could not read monitoring information");
+
+ SearchResultEntry entry = op.getSearchEntries().getFirst();
+
+ if (entry == null)
+ throw new Exception("Could not find monitoring entry");
+
+ /*
+ * Find the multi valued attribute matching the requested assured mode
+ */
+ String assuredAttr = null;
+ switch(assuredMode)
+ {
+ case SAFE_READ_MODE:
+ assuredAttr = "assured-sr-server-not-acknowledged-updates";
+ break;
+ case SAFE_DATA_MODE:
+ assuredAttr = "assured-sd-server-timeout-updates";
+ break;
+ default:
+ throw new Exception("Unknown assured type");
+ }
+
+ List<Attribute> attrs = entry.getAttribute(assuredAttr);
+
+ Map<Short,Integer> resultMap = new HashMap<Short,Integer>();
+ if ( (attrs == null) || (attrs.isEmpty()) )
+ return resultMap; // Empty map
+
+ Attribute attr = attrs.get(0);
+ Iterator<AttributeValue> attValIt = attr.iterator();
+ // Parse and store values
+ while (attValIt.hasNext())
+ {
+ String srvStr = attValIt.next().getStringValue();
+ StringTokenizer strtok = new StringTokenizer(srvStr, ":");
+ String token = strtok.nextToken();
+ if (token != null)
+ {
+ Short serverId = Short.valueOf(token);
+ token = strtok.nextToken();
+ if (token != null)
+ {
+ Integer nerrors = Integer.valueOf(token);
+ resultMap.put(serverId, nerrors);
+ }
+ }
+ }
+
+ return resultMap;
+ }
+}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ComputeBestServerTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ComputeBestServerTest.java
index 5057812..38b1bf4 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ComputeBestServerTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ComputeBestServerTest.java
@@ -27,7 +27,7 @@
package org.opends.server.replication.plugin;
import java.util.HashMap;
-import static org.opends.server.replication.plugin.ReplicationBroker.*;
+import static org.opends.server.replication.service.ReplicationBroker.*;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.ErrorLogger.logError;
import static org.opends.server.loggers.debug.DebugLogger.getTracer;
@@ -40,8 +40,6 @@
import org.opends.server.replication.ReplicationTestCase;
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.common.ServerState;
-import org.opends.server.replication.plugin.ReplicationBroker.ServerInfo;
-import org.opends.server.types.DN;
import org.testng.annotations.Test;
/**
@@ -85,7 +83,7 @@
final String WINNER = "winner";
// Create my state
- ServerState mySt = new ServerState();
+ ServerState mySt = new ServerState();
ChangeNumber cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo
mySt.update(cn);
cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo
@@ -103,7 +101,7 @@
rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
@@ -148,7 +146,7 @@
rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
@@ -170,7 +168,7 @@
short myId1 = 1;
short myId2 = 2;
short myId3 = 3;
-
+
// definitions for server names
final String WINNER = "winner";
@@ -195,7 +193,7 @@
rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
@@ -242,7 +240,7 @@
rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
@@ -300,7 +298,7 @@
rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
@@ -360,11 +358,11 @@
rsInfos.put(LOOSER1, new ServerInfo(aState, (byte)2));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
-
+
/**
* Test with 2 replication servers, none of them from our group id.
*
@@ -418,7 +416,7 @@
rsInfos.put(WINNER, new ServerInfo(aState, (byte)2));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
@@ -487,11 +485,11 @@
rsInfos.put(LOOSER2, new ServerInfo(aState, (byte)1));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
-
+
/**
* Test with 3 replication servers, up to date, but 2 different group ids.
*
@@ -558,7 +556,7 @@
rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
@@ -605,7 +603,7 @@
rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
@@ -663,11 +661,11 @@
rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
-
+
/**
* Test with 3 replication servers, late.
*
@@ -732,11 +730,11 @@
rsInfos.put(LOOSER2, new ServerInfo(aState, (byte)1));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
-
+
/**
* Test with 6 replication servers, some up, some late, one null
*
@@ -752,8 +750,8 @@
// definitions for server ids
short myId1 = 1;
short myId2 = 2;
- short myId3 = 3;
-
+ short myId3 = 3;
+
// definitions for server names
final String WINNER = "winner";
final String LOOSER1 = "looser1";
@@ -803,7 +801,7 @@
cn = new ChangeNumber(10L, 0, myId3);
aState.update(cn);
rsInfos.put(LOOSER3, new ServerInfo(aState, (byte)1));
-
+
// State for server 4
aState = new ServerState();
cn = new ChangeNumber(6L, 0, myId1);
@@ -813,7 +811,7 @@
cn = new ChangeNumber(8L, 0, myId3);
aState.update(cn);
rsInfos.put(WINNER, new ServerInfo(aState, (byte)1));
-
+
// State for server 5 (null one for our serverid)
aState = new ServerState();
cn = new ChangeNumber(5L, 0, myId2);
@@ -821,7 +819,7 @@
cn = new ChangeNumber(5L, 0, myId3);
aState.update(cn);
rsInfos.put(LOOSER4, new ServerInfo(aState, (byte)1));
-
+
// State for server 6
aState = new ServerState();
cn = new ChangeNumber(5L, 0, myId1);
@@ -833,7 +831,7 @@
rsInfos.put(LOOSER5, new ServerInfo(aState, (byte)1));
String bestServer =
- computeBestReplicationServer(mySt, rsInfos, myId1, new DN(), (byte)1);
+ computeBestReplicationServer(mySt, rsInfos, myId1, " ", (byte)1);
assertEquals(bestServer, WINNER, "Wrong best replication server.");
}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/GroupIdHandshakeTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/GroupIdHandshakeTest.java
index 92fba1a..5da9444 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/GroupIdHandshakeTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/GroupIdHandshakeTest.java
@@ -30,7 +30,7 @@
import java.net.ServerSocket;
import java.util.SortedSet;
import java.util.TreeSet;
-import static org.opends.server.replication.plugin.ReplicationBroker.*;
+
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.ErrorLogger.logError;
import static org.opends.server.loggers.debug.DebugLogger.getTracer;
@@ -64,8 +64,8 @@
private int rs1Port = -1;
private int rs2Port = -1;
private int rs3Port = -1;
- private ReplicationDomain rd1 = null;
- private ReplicationDomain rd2 = null;
+ private LDAPReplicationDomain rd1 = null;
+ private LDAPReplicationDomain rd2 = null;
private ReplicationServer rs1 = null;
private ReplicationServer rs2 = null;
private ReplicationServer rs3 = null;
@@ -147,16 +147,6 @@
rs3Port = -1;
}
- private void sleep(long time)
- {
- try
- {
- Thread.sleep(time);
- } catch (InterruptedException ex)
- {
- fail("Error sleeping " + stackTraceToSingleLineString(ex));
- }
- }
/**
* Check connection of the provided replication domain to the provided
@@ -167,7 +157,7 @@
{
int rsPort = -1;
- ReplicationDomain rd = null;
+ LDAPReplicationDomain rd = null;
switch (dsId)
{
case DS1_ID:
@@ -281,7 +271,7 @@
private SortedSet<String> createRSListForTestCase(String testCase)
{
SortedSet<String> replServers = new TreeSet<String>();
-
+
if (testCase.equals("testRSWithSameGroupIds"))
{
// 2 servers used for this test case.
@@ -369,7 +359,7 @@
/**
* Creates a new ReplicationDomain.
*/
- private ReplicationDomain createReplicationDomain(short serverId,
+ private LDAPReplicationDomain createReplicationDomain(short serverId,
int groupId, String testCase)
{
@@ -380,7 +370,7 @@
DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
DomainFakeCfg domainConf =
new DomainFakeCfg(baseDn, serverId, replServers, groupId);
- ReplicationDomain replicationDomain =
+ LDAPReplicationDomain replicationDomain =
MultimasterReplication.createNewDomain(domainConf);
replicationDomain.start();
return replicationDomain;
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalCsnOrderingTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalCsnOrderingTest.java
index f9c2ecf..4789ca0 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalCsnOrderingTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalCsnOrderingTest.java
@@ -158,7 +158,7 @@
throws Exception
{
// Creates a rule
- HistoricalCsnOrderingMatchingRule r =
+ HistoricalCsnOrderingMatchingRule r =
new HistoricalCsnOrderingMatchingRule();
ChangeNumber del1 = new ChangeNumber(1, (short) 0, (short) 1);
@@ -178,7 +178,7 @@
}
/**
- * Test that we can retrieve the entries that were missed by
+ * Test that we can retrieve the entries that were missed by
* a replication server and can re-build operations from the historical
* informations.
*/
@@ -227,7 +227,7 @@
logError(Message.raw(Category.SYNC, Severity.INFORMATION,
"First historical value:" + histValue));
- // Perform a 2nd modification to update the hist attribute with
+ // Perform a 2nd modification to update the hist attribute with
// a second value
resultCode = TestCaseUtils.applyModifications(false,
"dn: cn=test1," + baseDn.toString(),
@@ -256,7 +256,8 @@
// Retrieves the entries that have changed since the first modification
InternalSearchOperation op =
- ReplicationBroker.searchForChangedEntries(baseDn, fromChangeNumber, null);
+ LDAPReplicationDomain.searchForChangedEntries(
+ baseDn, fromChangeNumber, null);
// The expected result is one entry .. the one previously modified
assertEquals(op.getResultCode(), ResultCode.SUCCESS);
@@ -279,6 +280,6 @@
updatesCnt++;
}
}
- assertTrue(updatesCnt == 3);
+ assertTrue(updatesCnt == 3);
}
}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalTest.java
index b756b5a..8f0d85f 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/HistoricalTest.java
@@ -27,11 +27,13 @@
package org.opends.server.replication.plugin;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
+
import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.service.ReplicationBroker;
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.protocol.AddMsg;
import org.opends.server.replication.protocol.ModifyMsg;
-import org.opends.server.replication.protocol.UpdateMsg;
import org.opends.server.TestCaseUtils;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.tools.LDAPModify;
@@ -335,7 +337,7 @@
ModifyMsg modMsg = new ModifyMsg(changeNum, dn, mods, entryuuid);
broker.publish(modMsg);
}
-
+
/**
* Test that historical information is correctly added when performaing ADD,
* MOD and MODDN operations.
@@ -416,7 +418,7 @@
for (FakeOperation fake : ops)
{
- UpdateMsg msg = (UpdateMsg) fake.generateMessage();
+ LDAPUpdateMsg msg = (LDAPUpdateMsg) fake.generateMessage();
AbstractOperation op =
msg.createOperation(InternalClientConnection.getRootConnection());
op.setInternalOperation(true);
@@ -449,7 +451,7 @@
AddMsg addmsg = addOp.generateMessage();
assertTrue(dn1.equals(DN.decode(addmsg.getDn())));
assertTrue(addmsg.getUniqueId().equals(Historical.getEntryUuid(entry)));
- String parentId = ReplicationDomain.findEntryId(dn1.getParent());
+ String parentId = LDAPReplicationDomain.findEntryId(dn1.getParent());
assertTrue(addmsg.getParentUid().equals(parentId));
addmsg.createOperation(InternalClientConnection.getRootConnection());
} else
@@ -462,7 +464,7 @@
}
}
}
-
+
assertEquals(count, assertCount);
}
}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/IsolationTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/IsolationTest.java
index 611257d..6a0f891 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/IsolationTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/IsolationTest.java
@@ -26,15 +26,10 @@
*/
package org.opends.server.replication.plugin;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
+
import java.net.ServerSocket;
import java.util.SortedSet;
import java.util.TreeSet;
-import java.util.UUID;
import static org.testng.Assert.*;
@@ -65,7 +60,7 @@
@Test()
public void noUpdateIsolationPolicyTest() throws Exception
{
- ReplicationDomain domain = null;
+ LDAPReplicationDomain domain = null;
DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
SynchronizationProvider replicationPlugin = null;
short serverId = 1;
@@ -106,7 +101,7 @@
op = conn.processModify(baseDn, generatemods("description", "test"));
// check that the operation was successful.
- assertEquals(op.getResultCode(), ResultCode.SUCCESS,
+ assertEquals(op.getResultCode(), ResultCode.SUCCESS,
op.getAdditionalLogMessage().toString());
}
finally
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ModifyConflictTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ModifyConflictTest.java
index e2f7039..b0bf2d4 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ModifyConflictTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ModifyConflictTest.java
@@ -26,6 +26,8 @@
*/
package org.opends.server.replication.plugin;
+import org.opends.server.replication.protocol.LDAPUpdateMsg;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
@@ -49,7 +51,6 @@
import org.opends.server.replication.plugin.Historical;
import org.opends.server.replication.protocol.ModifyContext;
import org.opends.server.replication.protocol.ReplicationMsg;
-import org.opends.server.replication.protocol.UpdateMsg;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeBuilder;
import org.opends.server.types.AttributeType;
@@ -1137,9 +1138,9 @@
assertTrue(new FakeOperationComparator().compare(fk, fk) == 0);
assertTrue(new FakeOperationComparator().compare(null , fk) < 0);
ReplicationMsg generatedMsg = fk.generateMessage() ;
- if (generatedMsg instanceof UpdateMsg)
+ if (generatedMsg instanceof LDAPUpdateMsg)
{
- UpdateMsg new_name = (UpdateMsg) generatedMsg;
+ LDAPUpdateMsg new_name = (LDAPUpdateMsg) generatedMsg;
assertEquals(new_name.getUniqueId(),uuid);
}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentServerStateTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentServerStateTest.java
index e447cb8..0d1ba91 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentServerStateTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentServerStateTest.java
@@ -28,6 +28,8 @@
import static org.testng.Assert.assertEquals;
+import org.opends.server.replication.common.ServerState;
+
import org.opends.server.replication.ReplicationTestCase;
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.common.ChangeNumberGenerator;
@@ -69,9 +71,13 @@
* 2 ChangeNumbers have been saved in this new PersistentServerState.
*/
DN baseDn = DN.decode(dn);
- PersistentServerState state = new PersistentServerState(baseDn, (short) 1);
- ChangeNumberGenerator gen1 = new ChangeNumberGenerator((short) 1, state);
- ChangeNumberGenerator gen2 = new ChangeNumberGenerator((short) 2, state);
+ ServerState origState = new ServerState();
+ PersistentServerState state =
+ new PersistentServerState(baseDn, (short) 1, origState);
+ ChangeNumberGenerator gen1 =
+ new ChangeNumberGenerator((short) 1, origState);
+ ChangeNumberGenerator gen2 =
+ new ChangeNumberGenerator((short) 2, origState);
ChangeNumber cn1 = gen1.newChangeNumber();
ChangeNumber cn2 = gen2.newChangeNumber();
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationServerFailoverTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationServerFailoverTest.java
index 004c798..442257b 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationServerFailoverTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ReplicationServerFailoverTest.java
@@ -63,8 +63,8 @@
private static final short RS2_ID = 32;
private int rs1Port = -1;
private int rs2Port = -1;
- private ReplicationDomain rd1 = null;
- private ReplicationDomain rd2 = null;
+ private LDAPReplicationDomain rd1 = null;
+ private LDAPReplicationDomain rd2 = null;
private ReplicationServer rs1 = null;
private ReplicationServer rs2 = null;
@@ -298,7 +298,7 @@
{
int rsPort = -1;
- ReplicationDomain rd = null;
+ LDAPReplicationDomain rd = null;
switch (dsId)
{
case DS1_ID:
@@ -439,7 +439,7 @@
/**
* Creates a new ReplicationDomain.
*/
- private ReplicationDomain createReplicationDomain(DN baseDn, short serverId)
+ private LDAPReplicationDomain createReplicationDomain(DN baseDn, short serverId)
{
SortedSet<String> replServers = new TreeSet<String>();
@@ -452,7 +452,7 @@
DomainFakeCfg domainConf =
new DomainFakeCfg(baseDn, serverId, replServers);
//domainConf.setHeartbeatInterval(500);
- ReplicationDomain replicationDomain =
+ LDAPReplicationDomain replicationDomain =
MultimasterReplication.createNewDomain(domainConf);
replicationDomain.start();
@@ -465,7 +465,7 @@
return null;
}
- private int findReplServerConnected(ReplicationDomain rd)
+ private int findReplServerConnected(LDAPReplicationDomain rd)
{
int rsPort = -1;
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/StateMachineTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/StateMachineTest.java
index e1d614b..0a103da 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/StateMachineTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/StateMachineTest.java
@@ -35,7 +35,7 @@
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
-import static org.opends.server.replication.plugin.ReplicationBroker.*;
+
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.ErrorLogger.logError;
import static org.opends.server.loggers.debug.DebugLogger.getTracer;
@@ -48,6 +48,7 @@
import org.opends.server.TestCaseUtils;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.service.ReplicationBroker;
import org.opends.server.replication.common.ChangeNumberGenerator;
import org.opends.server.replication.common.DSInfo;
import org.opends.server.replication.common.ServerState;
@@ -60,7 +61,6 @@
import org.opends.server.replication.protocol.ReplicationMsg;
import org.opends.server.replication.protocol.ResetGenerationIdMsg;
import org.opends.server.replication.protocol.RoutableMsg;
-import org.opends.server.replication.protocol.TopologyMsg;
import org.opends.server.replication.server.ReplServerFakeConfiguration;
import org.opends.server.replication.server.ReplicationServer;
import org.opends.server.types.Attribute;
@@ -71,7 +71,6 @@
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
-import static org.opends.server.TestCaseUtils.*;
/**
* Some tests to go through the DS state machine and validate we get the
@@ -87,7 +86,7 @@
private static final short DS3_ID = 3;
private static final short RS1_ID = 41;
private int rs1Port = -1;
- private ReplicationDomain ds1 = null;
+ private LDAPReplicationDomain ds1 = null;
private ReplicationBroker ds2 = null;
private ReplicationBroker ds3 = null;
private ReplicationServer rs1 = null;
@@ -176,7 +175,7 @@
{
ReplicationBroker rb = null;
- ReplicationDomain rd = null;
+ LDAPReplicationDomain rd = null;
switch (dsId)
{
case DS1_ID:
@@ -215,14 +214,14 @@
// Sleep 1 second
try
{
- Thread.sleep(1000);
+ Thread.sleep(100);
} catch (InterruptedException ex)
{
fail("Error sleeping " + stackTraceToSingleLineString(ex));
}
nSec++;
- if (nSec > secTimeout)
+ if (nSec > secTimeout*10)
{
// Timeout reached, end with error
fail("checkConnection: DS " + dsId + " is not connected to the RS after "
@@ -276,7 +275,7 @@
* Creates and starts a new ReplicationDomain configured for the replication
* server
*/
- private ReplicationDomain createReplicationDomain(short dsId)
+ private LDAPReplicationDomain createReplicationDomain(short dsId)
{
try
{
@@ -286,7 +285,7 @@
DN baseDn = DN.decode(EXAMPLE_DN);
DomainFakeCfg domainConf =
new DomainFakeCfg(baseDn, dsId, replServers);
- ReplicationDomain replicationDomain =
+ LDAPReplicationDomain replicationDomain =
MultimasterReplication.createNewDomain(domainConf);
replicationDomain.start();
@@ -308,7 +307,7 @@
throws Exception, SocketException
{
ReplicationBroker broker = new ReplicationBroker(null,
- state, DN.decode(EXAMPLE_DN), dsId, 0, 0, 0, 0, window, 0, generationId,
+ state, EXAMPLE_DN, dsId, 100, generationId, 0,
new ReplSessionSecurity(null, null, null, true), (byte) 1);
ArrayList<String> servers = new ArrayList<String>(1);
servers.add("localhost:" + rs1Port);
@@ -413,7 +412,6 @@
try
{
-
/**
* RS1 starts with specified threshold value
*/
@@ -440,7 +438,7 @@
// having sent them to TCP receive queue of DS2.
bw = new BrokerWriter(ds3, DS3_ID, false);
bw.followAndPause(11);
- sleep(1000);
+ // sleep(1000);
/**
* DS3 sends changes (less than threshold): DS2 should still be in normal
@@ -452,10 +450,10 @@
{
nChangesSent = thresholdValue - 1;
bw.followAndPause(nChangesSent);
- sleep(7000); // Be sure status analyzer has time to test
+ sleep(1000); // Be sure status analyzer has time to test
ReplicationMsg msg = br3.getLastMsg();
debugInfo(testCase + " Step 1: last message from writer: " + msg);
- assertTrue(msg == null);
+ assertTrue(msg == null, (msg != null) ? msg.toString() : "null" );
}
/**
@@ -463,17 +461,29 @@
* update topo message with status of DS2: degraded status
*/
bw.followAndPause(thresholdValue - nChangesSent);
- sleep(7000); // Be sure status analyzer has time to test
- ReplicationMsg lastMsg = br3.getLastMsg();
- assertTrue(lastMsg != null);
- debugInfo(testCase + " Step 2: last message from writer: " + lastMsg);
- assertTrue(lastMsg instanceof TopologyMsg);
- TopologyMsg topoMsg = (TopologyMsg) lastMsg;
- List<DSInfo> dsList = topoMsg.getDsList();
- assertEquals(dsList.size(), 1);
- DSInfo ds3Info = dsList.get(0);
- assertEquals(ds3Info.getDsId(), DS2_ID);
- assertEquals(ds3Info.getStatus(), ServerStatus.DEGRADED_STATUS);
+ // wait for a status MSG status analyzer to broker 3
+ ReplicationMsg lastMsg = null;
+ for (int count = 0; count< 50; count++)
+ {
+ List<DSInfo> dsList = ds3.getDsList();
+ DSInfo ds3Info = null;
+ if (dsList.size() > 0)
+ {
+ ds3Info = dsList.get(0);
+ }
+ if ((ds3Info != null) && (ds3Info.getDsId() == DS2_ID) &&
+ (ds3Info.getStatus()== ServerStatus.DEGRADED_STATUS) )
+ {
+ break;
+ }
+ else
+ {
+ if (count < 50)
+ sleep(200); // Be sure status analyzer has time to test
+ else
+ fail("DS2 did not get degraded : " + ds3Info);
+ }
+ }
/**
* DS3 sends 10 additional changes after threshold value, DS2 should still be
@@ -481,7 +491,7 @@
*/
bw.followAndPause(10);
bw.shutdown();
- sleep(7000); // Be sure status analyzer has time to test
+ sleep(1000); // Be sure status analyzer has time to test
lastMsg = br3.getLastMsg();
ReplicationMsg msg = br3.getLastMsg();
debugInfo(testCase + " Step 3: last message from writer: " + msg);
@@ -492,17 +502,28 @@
* (create a reader to emulate replay of messages (messages read from queue))
*/
br2 = new BrokerReader(ds2, DS2_ID);
- sleep(7000); // Be sure messages are read and status analyzer has time to test
- lastMsg = br3.getLastMsg();
- assertTrue(lastMsg != null);
- debugInfo(testCase + " Step 4: last message from writer: " + lastMsg);
- assertTrue(lastMsg instanceof TopologyMsg);
- topoMsg = (TopologyMsg) lastMsg;
- dsList = topoMsg.getDsList();
- assertEquals(dsList.size(), 1);
- ds3Info = dsList.get(0);
- assertEquals(ds3Info.getDsId(), DS2_ID);
- assertEquals(ds3Info.getStatus(), ServerStatus.NORMAL_STATUS);
+ // wait for a status MSG status analyzer to broker 3
+ for (int count = 0; count< 50; count++)
+ {
+ List<DSInfo> dsList = ds3.getDsList();
+ DSInfo ds3Info = null;
+ if (dsList.size() > 0)
+ {
+ ds3Info = dsList.get(0);
+ }
+ if ((ds3Info != null) && (ds3Info.getDsId() == DS2_ID) &&
+ (ds3Info.getStatus()== ServerStatus.DEGRADED_STATUS) )
+ {
+ break;
+ }
+ else
+ {
+ if (count < 50)
+ sleep(200); // Be sure status analyzer has time to test
+ else
+ fail("DS2 did not get degraded.");
+ }
+ }
} finally
{
@@ -617,8 +638,8 @@
rs1.remove();
bw.pause();
sleepAssertStatusEquals(30, ds1, ServerStatus.NOT_CONNECTED_STATUS);
-
-
+
+
/**
* DS2 restarts with up to date server state (this allows to have
* restarting RS1 not sending him some updates he already sent)
@@ -680,7 +701,7 @@
* DS2 sends reset gen id order with bad gen id: DS1 should go in bad gen id
* status (from degraded status this time)
*/
- resetGenId(ds2, -1); // -1 to allow next step full update and flush RS db so that DS1 can reconnect after full update
+ resetGenId(ds2, -1); // -1 to allow next step full update and flush RS db so that DS1 can reconnect after full update
sleepAssertStatusEquals(30, ds1, ServerStatus.BAD_GEN_ID_STATUS);
bw.pause();
@@ -705,7 +726,7 @@
ds2.stop(); // will need a new broker with another gen id restart it
bw.shutdown();
br.shutdown();
- long newGen = ds1.getGenerationId();
+ long newGen = ds1.getGenerationID();
curState = ds1.getServerState();
ds2 = createReplicationBroker(DS2_ID, curState, newGen);
checkConnection(30, DS2_ID);
@@ -738,7 +759,7 @@
ds2.stop(); // will need a new broker with another gen id restart it
bw.shutdown();
br.shutdown();
- newGen = ds1.getGenerationId();
+ newGen = ds1.getGenerationID();
curState = ds1.getServerState();
ds2 = createReplicationBroker(DS2_ID, curState, newGen);
checkConnection(30, DS2_ID);
@@ -798,7 +819,7 @@
// a backend backed up with a file
// Clear the backend
- ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+ LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
}
@@ -815,8 +836,8 @@
super.classCleanUp();
// Clear the backend
- ReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
-
+ LDAPReplicationDomain.clearJEBackend(false, "userRoot", EXAMPLE_DN);
+
paranoiaCheck();
}
@@ -846,7 +867,7 @@
private short destId = -1; // Server id of server to initialize
private long nEntries = -1; // Number of entries to send to dest
private boolean createReader = false;
-
+
/**
* If the BrokerInitializer is to be used for a lot of entries to send
* (which is often the case), the reader thread should be enabled to make
@@ -855,7 +876,7 @@
* method of the broker himself.
*/
private BrokerReader reader = null;
-
+
/**
* Creates a broker initializer with a reader
*/
@@ -874,7 +895,7 @@
this.serverId = serverId;
this.createReader = createReader;
}
-
+
/**
* Initializes a full update session by sending InitializeTargetMsg
*/
@@ -893,15 +914,11 @@
// Send init msg to warn dest server it is going do be initialized
RoutableMsg initTargetMsg = null;
- try
- {
- initTargetMsg =
- new InitializeTargetMsg(DN.decode(EXAMPLE_DN), serverId, destId,
+
+ initTargetMsg =
+ new InitializeTargetMsg(EXAMPLE_DN, serverId, destId,
serverId, nEntries);
- } catch (DirectoryException ex)
- {
- fail("Unable to create init message.");
- }
+
rb.publish(initTargetMsg);
// Send top entry for the domain
@@ -951,27 +968,27 @@
public void runFullUpdate()
{
debugInfo("Broker " + serverId + " initializer starting sending entries to server " + destId);
-
+
for(long i = 0 ; i<nEntries ; i++) {
EntryMsg entryMsg = createNextEntryMsg();
rb.publish(entryMsg);
}
debugInfo("Broker " + serverId + " initializer stopping sending entries");
-
+
debugInfo("Broker " + serverId + " initializer sending EntryDoneMsg");
DoneMsg doneMsg = new DoneMsg(serverId, destId);
rb.publish(doneMsg);
-
+
if (createReader)
{
reader.shutdown();
}
-
+
debugInfo("Broker " + serverId + " initializer thread is dying");
}
}
-
+
/**
* Thread for sending a lot of changes through a broker.
*/
@@ -992,6 +1009,7 @@
private int nChangesSent = 0; // Number of sent changes
private int nChangesSentLimit = 0;
ChangeNumberGenerator gen = null;
+ private Object sleeper = new Object();
/**
* If the BrokerWriter is to be used for a lot of changes to send (which is
* often the case), the reader thread should be enabled to make the window
@@ -1084,7 +1102,10 @@
try
{
// Writer in pause, sleep a while to let other threads work
- Thread.sleep(1000);
+ synchronized(sleeper)
+ {
+ sleeper.wait(1000);
+ }
} catch (InterruptedException ex)
{
/* Don't care */
@@ -1100,6 +1121,10 @@
{
suspended.set(true); // If were working
shutdown.set(true);
+ synchronized (sleeper)
+ {
+ sleeper.notify();
+ }
try
{
join();
@@ -1107,7 +1132,7 @@
{
/* Don't care */
}
-
+
// Stop reader if any
if (reader != null)
{
@@ -1128,12 +1153,12 @@
{
try
{
- Thread.sleep(1000);
+ Thread.sleep(200);
} catch (InterruptedException ex)
{
/* Don't care */
}
- }
+ }
}
/**
@@ -1183,7 +1208,7 @@
*/
public void followAndPause(int nChanges)
{
- debugInfo("Requested broker writer " + serverId + " to write " + nChanges + " change(s).");
+ debugInfo("Requested broker writer " + serverId + " to write " + nChanges + " change(s).");
pause(); // If however we were already working
// Initialize counter system variables
@@ -1341,7 +1366,7 @@
* @param testedValue The value we want to test
* @param expectedValue The value the tested value should be equal to
*/
- private void sleepAssertStatusEquals(int secTimeout, ReplicationDomain testedValue,
+ private void sleepAssertStatusEquals(int secTimeout, LDAPReplicationDomain testedValue,
ServerStatus expectedValue)
{
int nSec = 0;
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
index da99b8e..5acfae9 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/TopologyViewTest.java
@@ -32,7 +32,7 @@
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
-import static org.opends.server.replication.plugin.ReplicationBroker.*;
+
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.ErrorLogger.logError;
import static org.opends.server.loggers.debug.DebugLogger.getTracer;
@@ -114,20 +114,20 @@
static
{
DS2_RU.add("ldap://fake_url_for_ds2");
-
+
DS6_RU.add("ldap://fake_url_for_ds6_A");
DS6_RU.add("ldap://fake_url_for_ds6_B");
}
-
+
private int rs1Port = -1;
private int rs2Port = -1;
private int rs3Port = -1;
- private ReplicationDomain rd1 = null;
- private ReplicationDomain rd2 = null;
- private ReplicationDomain rd3 = null;
- private ReplicationDomain rd4 = null;
- private ReplicationDomain rd5 = null;
- private ReplicationDomain rd6 = null;
+ private LDAPReplicationDomain rd1 = null;
+ private LDAPReplicationDomain rd2 = null;
+ private LDAPReplicationDomain rd3 = null;
+ private LDAPReplicationDomain rd4 = null;
+ private LDAPReplicationDomain rd5 = null;
+ private LDAPReplicationDomain rd6 = null;
private ReplicationServer rs1 = null;
private ReplicationServer rs2 = null;
private ReplicationServer rs3 = null;
@@ -203,7 +203,7 @@
rd6.shutdown();
rd6 = null;
}
-
+
try
{
// Clear any reference to a domain in synchro plugin
@@ -257,7 +257,7 @@
private void checkConnection(int secTimeout, short dsId, short rsId)
{
int rsPort = -1;
- ReplicationDomain rd = null;
+ LDAPReplicationDomain rd = null;
switch (dsId)
{
case DS1_ID:
@@ -398,7 +398,7 @@
}
return replServers;
- }
+ }
/**
* Creates a new ReplicationServer.
@@ -447,7 +447,7 @@
* Creates and starts a new ReplicationDomain with the correct list of
* know RSs according to DS id
*/
- private ReplicationDomain createReplicationDomain(short dsId)
+ private LDAPReplicationDomain createReplicationDomain(short dsId)
{
try
{
@@ -523,7 +523,7 @@
DomainFakeCfg domainConf =
new DomainFakeCfg(baseDn, dsId, replServers, assuredType,
assuredSdLevel, groupId, 0, refUrls);
- ReplicationDomain replicationDomain =
+ LDAPReplicationDomain replicationDomain =
MultimasterReplication.createNewDomain(domainConf);
replicationDomain.start();
@@ -1047,7 +1047,7 @@
return new TopoView(dsList, rsList);
}
-
+
/**
* Get the topo view each DS in the provided ds list has and compares it
* with the theoretical topology view that every body should have at the time
@@ -1057,8 +1057,8 @@
{
for(short currentDsId : dsIdList)
{
- ReplicationDomain rd = null;
-
+ LDAPReplicationDomain rd = null;
+
switch (currentDsId)
{
case DS1_ID:
@@ -1082,7 +1082,7 @@
default:
fail("Unknown replication domain server id.");
}
-
+
/**
* Get the topo view of the current analyzed DS
*/
@@ -1096,7 +1096,7 @@
dsList.add(aDsInfo);
}
short dsId = rd.getServerId();
- short rsId = rd.getBroker().getRsServerId();
+ short rsId = rd.getRsServerId();
ServerStatus status = rd.getStatus();
boolean assuredFlag = rd.isAssured();
AssuredMode assuredMode = rd.getAssuredMode();
@@ -1106,17 +1106,17 @@
DSInfo dsInfo = new DSInfo(dsId, rsId, TEST_DN_WITH_ROOT_ENTRY_GENID, status, assuredFlag, assuredMode,
safeDataLevel, groupId, refUrls);
dsList.add(dsInfo);
-
+
TopoView dsTopoView = new TopoView(dsList, rd.getRsList());
-
+
/**
* Compare to what is the expected view
*/
-
+
assertEquals(dsTopoView, theoricalTopoView);
}
}
-
+
/**
* Bag class representing a view of the topology at a given time
* (who is connected to who, what are the config parameters...)
@@ -1124,22 +1124,22 @@
private class TopoView
{
private List<DSInfo> dsList = null;
- private List<RSInfo> rsList = null;
-
+ private List<RSInfo> rsList = null;
+
public TopoView(List<DSInfo> dsList, List<RSInfo> rsList)
{
assertNotNull(dsList);
assertNotNull(rsList);
-
+
this.dsList = dsList;
this.rsList = rsList;
}
-
+
public boolean equals(Object obj)
{
assertNotNull(obj);
assertFalse(obj.getClass() != this.getClass());
-
+
TopoView topoView = (TopoView) obj;
// Check dsList
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
index 519824d..9efdd80 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/ProtocolCompatibilityTest.java
@@ -94,12 +94,12 @@
@DataProvider(name="createReplServerStartData")
public Object [][] createReplServerStartData() throws Exception
{
- DN baseDN = DN.decode("o=test");
+ String baseDN = "o=test";
ServerState state = new ServerState();
state.update(new ChangeNumber((long)0, 0,(short)0));
Object[] set1 = new Object[] {(short)1, baseDN, 0, "localhost:8989", state, 0L, (byte)0, 0};
- baseDN = DN.decode("dc=example,dc=com");
+ baseDN = "dc=example,dc=com";
state = new ServerState();
state.update(new ChangeNumber((long)75, 5,(short)263));
Object[] set2 = new Object[] {(short)16, baseDN, 100, "anotherHost:1025", state, 1245L, (byte)25, 3456};
@@ -112,7 +112,7 @@
* using protocol V1 and V2 are working.
*/
@Test(dataProvider="createReplServerStartData")
- public void replServerStartMsgTest(short serverId, DN baseDN, int window,
+ public void replServerStartMsgTest(short serverId, String baseDN, int window,
String url, ServerState state, long genId, byte groupId, int degTh) throws Exception
{
// Create V2 message
@@ -167,7 +167,7 @@
assertEquals(msg.getGroupId(), v2Msg.getGroupId());
assertEquals(msg.getDegradedStatusThreshold(), v2Msg.getDegradedStatusThreshold());
}
-
+
@DataProvider(name = "createAddData")
public Object[][] createAddData() {
return new Object[][] {
@@ -250,7 +250,7 @@
// Check default value for only V2 fields
assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
- assertEquals(newMsg.getSafeDataLevel(), (byte)-1);
+ assertEquals(newMsg.getSafeDataLevel(), (byte)1);
// Set again only V2 fields
newMsg.setAssuredMode(assuredMode);
@@ -336,7 +336,7 @@
// Check default value for only V2 fields
assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
- assertEquals(newMsg.getSafeDataLevel(), (byte)-1);
+ assertEquals(newMsg.getSafeDataLevel(), (byte)1);
// Set again only V2 fields
newMsg.setAssuredMode(assuredMode);
@@ -356,7 +356,7 @@
assertEquals(msg.getAssuredMode(), v2Msg.getAssuredMode());
assertEquals(msg.getSafeDataLevel(), v2Msg.getSafeDataLevel());
}
-
+
/**
* Build some data for the ModifyMsg test below.
*/
@@ -469,7 +469,7 @@
// Check default value for only V2 fields
assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
- assertEquals(newMsg.getSafeDataLevel(), (byte)-1);
+ assertEquals(newMsg.getSafeDataLevel(), (byte)1);
// Set again only V2 fields
newMsg.setAssuredMode(assuredMode);
@@ -503,7 +503,7 @@
genModOpBasis.getAttachment(SYNCHROCONTEXT));
assertEquals(modOpBasis.getModifications(), genModOpBasis.getModifications());
}
-
+
@DataProvider(name = "createModifyDnData")
public Object[][] createModifyDnData() {
@@ -606,7 +606,7 @@
// Check default value for only V2 fields
assertEquals(newMsg.getAssuredMode(), AssuredMode.SAFE_DATA_MODE);
- assertEquals(newMsg.getSafeDataLevel(), (byte)-1);
+ assertEquals(newMsg.getSafeDataLevel(), (byte)1);
assertEquals(modDnOpBasis.getModifications(), mods);
assertTrue(genModDnOpBasis.getModifications() == null);
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
index 04d1417..00afcce 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
@@ -26,11 +26,8 @@
*/
package org.opends.server.replication.protocol;
-import static org.opends.server.replication.protocol.OperationContext.SYNCHROCONTEXT;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
+import static org.opends.server.replication.protocol.OperationContext.*;
+import static org.testng.Assert.*;
import java.util.Iterator;
import java.util.ArrayList;
@@ -227,7 +224,7 @@
assertEquals(msg.getAssuredMode(), assuredMode);
// Check safe data level
- assertTrue(msg.getSafeDataLevel() == -1);
+ assertTrue(msg.getSafeDataLevel() == 1);
msg.setSafeDataLevel(safeDataLevel);
assertTrue(msg.getSafeDataLevel() == safeDataLevel);
@@ -312,7 +309,7 @@
assertEquals(op.getRawEntryDN(), mod2.getRawEntryDN());
// Create an update message from this op
- DeleteMsg updateMsg = (DeleteMsg) UpdateMsg.generateMsg(op);
+ DeleteMsg updateMsg = (DeleteMsg) LDAPUpdateMsg.generateMsg(op);
assertEquals(msg.getChangeNumber(), updateMsg.getChangeNumber());
}
@@ -415,7 +412,7 @@
assertEquals(moddn1.getModifications(), moddn2.getModifications());
// Create an update message from this op
- ModifyDNMsg updateMsg = (ModifyDNMsg) UpdateMsg.generateMsg(localOp);
+ ModifyDNMsg updateMsg = (ModifyDNMsg) LDAPUpdateMsg.generateMsg(localOp);
assertEquals(msg.getChangeNumber(), updateMsg.getChangeNumber());
}
@@ -518,7 +515,7 @@
// Create an update message from this op
- AddMsg updateMsg = (AddMsg) UpdateMsg.generateMsg(addOp);
+ AddMsg updateMsg = (AddMsg) LDAPUpdateMsg.generateMsg(addOp);
assertEquals(msg.getChangeNumber(), updateMsg.getChangeNumber());
}
@@ -615,12 +612,11 @@
@DataProvider(name="createServerStartData")
public Object [][] createServerStartData() throws Exception
{
- DN baseDN = DN.decode(TEST_ROOT_DN_STRING);
+ String baseDN = TEST_ROOT_DN_STRING;
ServerState state = new ServerState();
state.update(new ChangeNumber((long)0, 0,(short)0));
Object[] set1 = new Object[] {(short)1, baseDN, 0, state, 0L, false, (byte)0};
- baseDN = DN.decode(TEST_ROOT_DN_STRING);
state = new ServerState();
state.update(new ChangeNumber((long)75, 5,(short)263));
Object[] set2 = new Object[] {(short)16, baseDN, 100, state, 1248L, true, (byte)31};
@@ -633,7 +629,7 @@
* by checking that : msg == new ServerStartMsg(msg.getBytes()).
*/
@Test(dataProvider="createServerStartData")
- public void serverStartMsgTest(short serverId, DN baseDN, int window,
+ public void serverStartMsgTest(short serverId, String baseDN, int window,
ServerState state, long genId, boolean sslEncryption, byte groupId) throws Exception
{
ServerStartMsg msg = new ServerStartMsg(serverId, baseDN,
@@ -659,12 +655,11 @@
@DataProvider(name="createReplServerStartData")
public Object [][] createReplServerStartData() throws Exception
{
- DN baseDN = DN.decode(TEST_ROOT_DN_STRING);
+ String baseDN = TEST_ROOT_DN_STRING;
ServerState state = new ServerState();
state.update(new ChangeNumber((long)0, 0,(short)0));
Object[] set1 = new Object[] {(short)1, baseDN, 0, "localhost:8989", state, 0L, (byte)0, 0};
- baseDN = DN.decode(TEST_ROOT_DN_STRING);
state = new ServerState();
state.update(new ChangeNumber((long)75, 5,(short)263));
Object[] set2 = new Object[] {(short)16, baseDN, 100, "anotherHost:1025", state, 1245L, (byte)25, 3456};
@@ -677,7 +672,7 @@
* by checking that : msg == new ReplServerStartMsg(msg.getBytes()).
*/
@Test(dataProvider="createReplServerStartData")
- public void replServerStartMsgTest(short serverId, DN baseDN, int window,
+ public void replServerStartMsgTest(short serverId, String baseDN, int window,
String url, ServerState state, long genId, byte groupId, int degTh) throws Exception
{
ReplServerStartMsg msg = new ReplServerStartMsg(serverId,
@@ -734,7 +729,7 @@
List<String> urls3 = new ArrayList<String>();
urls3.add("ldaps://host:port/dc=foo??sub?(sn=One Entry)");
-
+
List<String> urls4 = new ArrayList<String>();
urls4.add("ldaps://host:port/dc=foobar1??sub?(sn=Another Entry 1)");
urls4.add("ldaps://host:port/dc=foobar2??sub?(sn=Another Entry 2)");
@@ -747,7 +742,7 @@
DSInfo dsInfo3 = new DSInfo((short)2436, (short)591, (long)0, ServerStatus.NORMAL_STATUS,
false, AssuredMode.SAFE_READ_MODE, (byte)17, (byte)0, urls3);
-
+
DSInfo dsInfo4 = new DSInfo((short)415, (short)146, (long)0, ServerStatus.BAD_GEN_ID_STATUS,
true, AssuredMode.SAFE_DATA_MODE, (byte)2, (byte)15, urls4);
@@ -862,7 +857,7 @@
assertTrue(msg.getSafeDataLevel() == newMsg.getSafeDataLevel());
assertEquals(msg.getReferralsURLs(), newMsg.getReferralsURLs());
}
-
+
/**
* Provider for the ChangeStatusMsg test
*/
@@ -897,6 +892,7 @@
{
HeartbeatMsg msg = new HeartbeatMsg();
HeartbeatMsg newMsg = new HeartbeatMsg(msg.getBytes());
+ assertNotNull(newMsg);
}
/**
@@ -909,7 +905,7 @@
ResetGenerationIdMsg newMsg = new ResetGenerationIdMsg(msg.getBytes());
assertEquals(msg.getGenerationId(), newMsg.getGenerationId());
}
-
+
/**
* Test MonitorRequestMsg encoding and decoding.
*/
@@ -918,6 +914,8 @@
{
MonitorRequestMsg msg = new MonitorRequestMsg((short)1,(short)2);
MonitorRequestMsg newMsg = new MonitorRequestMsg(msg.getBytes());
+ assertEquals(newMsg.getDestination(), 2);
+ assertEquals(newMsg.getsenderID(), 1);
}
/**
@@ -963,14 +961,14 @@
msg.setServerState(sid1, s1, now+1, true);
msg.setServerState(sid2, s2, now+2, true);
msg.setServerState(sid3, s3, now+3, false);
-
+
byte[] b = msg.getBytes();
MonitorMsg newMsg = new MonitorMsg(b);
assertEquals(rsState, msg.getReplServerDbState());
- assertEquals(newMsg.getReplServerDbState().toString(),
+ assertEquals(newMsg.getReplServerDbState().toString(),
msg.getReplServerDbState().toString());
-
+
Iterator<Short> it = newMsg.ldapIterator();
while (it.hasNext())
{
@@ -983,7 +981,7 @@
}
else if (sid == sid2)
{
- assertEquals(s.toString(), s2.toString());
+ assertEquals(s.toString(), s2.toString());
assertEquals((Long)(now+2), newMsg.getLDAPApproxFirstMissingDate(sid), "");
}
else
@@ -1025,7 +1023,7 @@
"objectclass: top\n" +
"objectclass: ds-task\n" +
"objectclass: ds-task-initialize\n" +
- "ds-task-class-name: org.opends.server.tasks.InitializeTask\n" +
+ "ds-task-class-name: org.opends.server.replication.api.InitializeTask\n" +
"ds-task-initialize-domain-dn: " + TEST_ROOT_DN_STRING + "\n" +
"ds-task-initialize-source: 1\n");
short sender = 1;
@@ -1047,7 +1045,7 @@
short sender = 1;
short target = 2;
InitializeRequestMsg msg = new InitializeRequestMsg(
- DN.decode(TEST_ROOT_DN_STRING), sender, target);
+ TEST_ROOT_DN_STRING, sender, target);
InitializeRequestMsg newMsg = new InitializeRequestMsg(msg.getBytes());
assertEquals(msg.getsenderID(), newMsg.getsenderID());
assertEquals(msg.getDestination(), newMsg.getDestination());
@@ -1064,10 +1062,9 @@
short targetID = 2;
short requestorID = 3;
long entryCount = 4;
- DN baseDN = DN.decode(TEST_ROOT_DN_STRING);
InitializeTargetMsg msg = new InitializeTargetMsg(
- baseDN, senderID, targetID, requestorID, entryCount);
+ TEST_ROOT_DN_STRING, senderID, targetID, requestorID, entryCount);
InitializeTargetMsg newMsg = new InitializeTargetMsg(msg.getBytes());
assertEquals(msg.getsenderID(), newMsg.getsenderID());
assertEquals(msg.getDestination(), newMsg.getDestination());
@@ -1079,7 +1076,7 @@
assertEquals(targetID, newMsg.getDestination());
assertEquals(requestorID, newMsg.getRequestorID());
assertEquals(entryCount, newMsg.getEntryCount());
- assertTrue(baseDN.equals(newMsg.getBaseDN())) ;
+ assertTrue(TEST_ROOT_DN_STRING.equals(newMsg.getBaseDN())) ;
}
@@ -1108,4 +1105,19 @@
assertEquals(msg.getMsgID(), newMsg.getMsgID());
assertEquals(msg.getDetails(), newMsg.getDetails());
}
+
+ /**
+ * Test Generic UpdateMsg
+ */
+ @Test
+ public void UpdateMsgTest() throws Exception
+ {
+ final String test = "string used for test";
+ UpdateMsg msg =
+ new UpdateMsg(
+ new ChangeNumber((long) 1, 2 , (short)3),
+ test.getBytes());
+ UpdateMsg newMsg = new UpdateMsg(msg.getBytes());
+ assertEquals(test.getBytes(), newMsg.getPayload());
+ }
}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/DbHandlerTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/DbHandlerTest.java
index 6861761..ff51863 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/DbHandlerTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/DbHandlerTest.java
@@ -34,7 +34,6 @@
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.common.ChangeNumberGenerator;
import org.opends.server.replication.protocol.DeleteMsg;
-import org.opends.server.types.DN;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import static org.opends.server.TestCaseUtils.*;
@@ -79,7 +78,7 @@
dbEnv = new ReplicationDbEnv(path, replicationServer);
- handler = new DbHandler((short) 1, DN.decode(TEST_ROOT_DN_STRING),
+ handler = new DbHandler((short) 1, TEST_ROOT_DN_STRING,
replicationServer, dbEnv, 5000);
ChangeNumberGenerator gen = new ChangeNumberGenerator((short) 1, 0);
@@ -173,7 +172,7 @@
dbEnv = new ReplicationDbEnv(path, replicationServer);
handler =
- new DbHandler((short) 1, DN.decode(TEST_ROOT_DN_STRING),
+ new DbHandler((short) 1, TEST_ROOT_DN_STRING,
replicationServer, dbEnv, 5000);
// Creates changes added to the dbHandler
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/MonitorTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/MonitorTest.java
index 1a38260..962af1c 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/MonitorTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/MonitorTest.java
@@ -50,9 +50,9 @@
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.service.ReplicationBroker;
import org.opends.server.replication.common.ChangeNumberGenerator;
-import org.opends.server.replication.plugin.ReplicationBroker;
-import org.opends.server.replication.plugin.ReplicationDomain;
+import org.opends.server.replication.plugin.LDAPReplicationDomain;
import org.opends.server.replication.protocol.AddMsg;
import org.opends.server.replication.protocol.ReplicationMsg;
import org.opends.server.replication.protocol.SocketSession;
@@ -93,7 +93,7 @@
private ReplicationServer replServer2 = null;
private ReplicationServer replServer3 = null;
private boolean emptyOldChanges = true;
- ReplicationDomain replDomain = null;
+ LDAPReplicationDomain replDomain = null;
SocketSession ssSession = null;
boolean ssShutdownRequested = false;
protected String[] updatedEntries;
@@ -240,7 +240,7 @@
"Unable to add the synchronized server");
configEntryList.add(synchroServerEntry.getDN());
- replDomain = ReplicationDomain.retrievesReplicationDomain(baseDn);
+ replDomain = LDAPReplicationDomain.retrievesReplicationDomain(baseDn);
if (replDomain != null)
{
@@ -393,7 +393,6 @@
debugInfo("Connecting DS to replServer1");
connectServer1ToChangelog(changelog1ID);
- Thread.sleep(1500);
try
{
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerDynamicConfTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerDynamicConfTest.java
index 028601c..d1aedcb 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerDynamicConfTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerDynamicConfTest.java
@@ -32,13 +32,13 @@
import org.opends.server.TestCaseUtils;
import org.opends.server.replication.ReplicationTestCase;
-import org.opends.server.replication.plugin.ReplicationBroker;
+import org.opends.server.replication.service.ReplicationBroker;
import org.opends.server.types.DN;
import org.testng.annotations.Test;
import static org.opends.server.TestCaseUtils.*;
/**
- * Tests that we can dynamically modify the configuration of replicationServer
+ * Tests that we can dynamically modify the configuration of replicationServer
*/
public class ReplicationServerDynamicConfTest extends ReplicationTestCase
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java
index 45560d0..948444d 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/ReplicationServerTest.java
@@ -63,12 +63,12 @@
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.service.ReplicationBroker;
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.common.ChangeNumberGenerator;
import org.opends.server.replication.common.ServerState;
import org.opends.server.replication.common.ServerStatus;
import org.opends.server.replication.plugin.MultimasterReplication;
-import org.opends.server.replication.plugin.ReplicationBroker;
import org.opends.server.replication.plugin.ReplicationServerListener;
import org.opends.server.replication.protocol.AddMsg;
import org.opends.server.replication.protocol.DeleteMsg;
@@ -192,14 +192,12 @@
{
replicationServer.clearDb();
changelogBasic();
- multipleWriterMultipleReader();
newClientLateServer1();
newClient();
newClientWithFirstChanges();
newClientWithChangefromServer1();
newClientWithChangefromServer2();
newClientWithUnknownChanges();
- oneWriterMultipleReader();
changelogChaining();
stopChangelog();
exportBackend();
@@ -207,7 +205,7 @@
windowProbeTest();
replicationServerConnected();
}
-
+
/**
* This test allows to check the behavior of the Replication Server
* when the DS disconnect and reconnect again.
@@ -255,7 +253,7 @@
server2 = openReplicationSession(
DN.decode(TEST_ROOT_DN_STRING), (short) 2, 100, replicationServerPort,
1000, true);
-
+
assertTrue(server1.isConnected());
assertTrue(server2.isConnected());
@@ -328,10 +326,6 @@
"other-uid");
server2.publish(msg);
msg2 = server1.receive();
- if (!(msg2 instanceof TopologyMsg))
- fail("ReplicationServer basic : incorrect message type received: " +
- msg2.getClass().toString() + ": content: " + msg2.toString());
- msg2 = server1.receive();
server1.updateWindowAfterReplay();
if (msg2 instanceof DeleteMsg)
{
@@ -359,7 +353,7 @@
else
fail("ReplicationServer basic : incorrect message type received: " +
msg2.getClass().toString() + ": content: " + msg2.toString());
-
+
debugInfo("Ending changelogBasic");
}
finally
@@ -564,9 +558,14 @@
* This test is configured by a relatively low stress
* but can be changed using TOTAL_MSG and CLIENT_THREADS consts.
*/
- private void oneWriterMultipleReader() throws Exception
+ @Test(enabled=true)
+ public void oneWriterMultipleReader() throws Exception
{
debugInfo("Starting oneWriterMultipleReader");
+
+ replicationServer.clearDb();
+ TestCaseUtils.initializeTestBackend(true);
+
ReplicationBroker server = null;
BrokerReader reader = null;
int TOTAL_MSG = 1000; // number of messages to send during the test
@@ -638,6 +637,9 @@
if (clientBroker[i] != null)
clientBroker[i].stop();
}
+
+ replicationServer.clearDb();
+ TestCaseUtils.initializeTestBackend(true);
}
}
@@ -652,7 +654,8 @@
* This test is configured for a relatively low stress
* but can be changed using TOTAL_MSG and THREADS consts.
*/
- private void multipleWriterMultipleReader() throws Exception
+ @Test()
+ public void multipleWriterMultipleReader() throws Exception
{
debugInfo("Starting multipleWriterMultipleReader");
final int TOTAL_MSG = 1000; // number of messages to send during the test
@@ -663,6 +666,9 @@
BrokerReader reader[] = new BrokerReader[THREADS];
ReplicationBroker broker[] = new ReplicationBroker[THREADS];
+ replicationServer.clearDb();
+ TestCaseUtils.initializeTestBackend(true);
+
try
{
/*
@@ -711,6 +717,9 @@
if (broker[i] != null)
broker[i].stop();
}
+
+ replicationServer.clearDb();
+ TestCaseUtils.initializeTestBackend(true);
}
}
@@ -789,7 +798,7 @@
// client2 will be created later
broker1 = openReplicationSession(DN.decode(TEST_ROOT_DN_STRING),
brokerIds[0], 100, changelogPorts[0], 1000, !emptyOldChanges);
-
+
assertTrue(broker1.isConnected());
if (itest == 0)
@@ -975,7 +984,7 @@
{
// send a ServerStartMsg with an empty ServerState.
ServerStartMsg msg =
- new ServerStartMsg((short) 1723, DN.decode(TEST_ROOT_DN_STRING),
+ new ServerStartMsg((short) 1723, TEST_ROOT_DN_STRING,
0, 0, 0, 0, WINDOW, (long) 5000, new ServerState(),
ProtocolVersion.getCurrentVersion(), 0, sslEncryption, (byte)-1);
session.publish(msg);
@@ -1021,7 +1030,7 @@
// received.
DN baseDn = DN.decode(TEST_ROOT_DN_STRING);
msg = new ServerStartMsg(
- (short) 1724, baseDn,
+ (short) 1724, TEST_ROOT_DN_STRING,
0, 0, 0, 0, WINDOW, (long) 5000, replServerState,
ProtocolVersion.getCurrentVersion(),
ReplicationTestCase.getGenerationId(baseDn),
@@ -1072,7 +1081,7 @@
* @throws Exception If the environment could not be set up.
*/
@AfterClass
-
+
public void classCleanUp() throws Exception
{
callParanoiaCheck = false;
@@ -1410,12 +1419,7 @@
* Testing searches on the backend of the replication server.
* @throws Exception
*/
- // TODO: this test disabled as testReplicationBackendACIs() is failing
- // : anonymous search returns entries from replication backend whereas it
- // should not. Probably a previous test in the nightlytests suite is
- // removing/modifying some ACIs...When problem foound, we have to re-enable
- // this test.
- @Test(enabled=false)
+ @Test(enabled=true)
public void searchBackend() throws Exception
{
debugInfo("Starting searchBackend");
@@ -1494,7 +1498,13 @@
LDAPFilter.decode("(changetype=*)"));
assertEquals(op.getResultCode(), ResultCode.NO_SUCH_OBJECT);
- testReplicationBackendACIs();
+ // TODO: testReplicationBackendACIs() is disabled because it
+ // is currently failing when run in the nightly target.
+ // anonymous search returns entries from replication backend whereas it
+ // should not. Probably a previous test in the nightlytests suite is
+ // removing/modifying some ACIs...When problem foound, we have to re-enable
+ // this test.
+ // testReplicationBackendACIs();
// General search
op = connection.processSearch(
@@ -1555,10 +1565,10 @@
assertEquals(op.getSearchEntries().size(), 1);
/*
- * It would be nice to be have the abilities to search for
- * entries in the replication backend using the DN on which the
+ * It would be nice to be have the abilities to search for
+ * entries in the replication backend using the DN on which the
* operation was done as the search criteria.
- * This is not possible yet, this part of the test is therefore
+ * This is not possible yet, this part of the test is therefore
* disabled.
*
* debugInfo("Query / searchBase");
@@ -1604,7 +1614,7 @@
attrs2);
assertEquals(op.getResultCode(), ResultCode.SUCCESS);
assertEquals(op.getSearchEntries().size(), 5);
-
+
debugInfo("Successfully ending searchBackend");
} finally {
@@ -1759,7 +1769,7 @@
broker2 = openReplicationSession(DN.decode(TEST_ROOT_DN_STRING),
brokerIds[1], 100, changelogPorts[1], 1000, emptyOldChanges);
-
+
assertTrue(broker1.isConnected());
assertTrue(broker2.isConnected());
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java
new file mode 100644
index 0000000..76c2cae
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeReplicationDomain.java
@@ -0,0 +1,149 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+import org.opends.server.types.ResultCode;
+import java.io.IOException;
+import java.util.concurrent.BlockingQueue;
+import org.opends.server.config.ConfigException;
+import java.util.Collection;
+import java.io.InputStream;
+import java.io.OutputStream;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.types.DirectoryException;
+import static org.opends.messages.ReplicationMessages.*;
+
+/**
+ * This class is the minimum implementation of a Concrete ReplicationDomain
+ * used to test the Generic Replication Service.
+ */
+public class FakeReplicationDomain extends ReplicationDomain
+{
+ // A blocking queue that is used to send the UpdateMsg received from
+ // the Replication Service.
+ BlockingQueue<UpdateMsg> queue = null;
+
+ // A string that will be exported should exportBackend be called.
+ String exportString = null;
+
+ // A StringBuilder that will be used to build a build a new String should the
+ // import be called.
+ StringBuilder importString = null;
+
+ private int exportedEntryCount;
+
+ public FakeReplicationDomain(
+ String serviceID,
+ short serverID,
+ Collection<String> replicationServers,
+ int window,
+ long heartbeatInterval,
+ BlockingQueue<UpdateMsg> queue) throws ConfigException
+ {
+ super(serviceID, serverID);
+ startPublishService(replicationServers, window, heartbeatInterval);
+ startListenService();
+ this.queue = queue;
+ }
+
+ public FakeReplicationDomain(
+ String serviceID,
+ short serverID,
+ Collection<String> replicationServers,
+ int window,
+ long heartbeatInterval,
+ String exportString,
+ StringBuilder importString,
+ int exportedEntryCount) throws ConfigException
+ {
+ super(serviceID, serverID);
+ startPublishService(replicationServers, window, heartbeatInterval);
+ startListenService();
+ this.exportString = exportString;
+ this.importString = importString;
+ this.exportedEntryCount = exportedEntryCount;
+ }
+
+ @Override
+ public long countEntries() throws DirectoryException
+ {
+ return exportedEntryCount;
+ }
+
+ @Override
+ protected void exportBackend(OutputStream output) throws DirectoryException
+ {
+ try
+ {
+ output.write(exportString.getBytes());
+ output.flush();
+ output.close();
+ }
+ catch (IOException e)
+ {
+ throw new DirectoryException(ResultCode.OPERATIONS_ERROR,
+ ERR_BACKEND_EXPORT_ENTRY.get("", ""));
+ }
+
+ }
+
+ @Override
+ public long getGenerationID()
+ {
+ return 1;
+ }
+
+ @Override
+ protected void importBackend(InputStream input) throws DirectoryException
+ {
+ byte[] buffer = new byte[1000];
+
+ int ret;
+ do
+ {
+ try
+ {
+ ret = input.read(buffer, 0, 1000);
+ } catch (IOException e)
+ {
+ throw new DirectoryException(
+ ResultCode.OPERATIONS_ERROR,
+ ERR_BACKEND_EXPORT_ENTRY.get("", ""));
+ }
+ importString.append(new String(buffer, 0, ret));
+ }
+ while (ret >= 0);
+ }
+
+ @Override
+ public boolean processUpdate(UpdateMsg updateMsg)
+ {
+ if (queue != null)
+ queue.add(updateMsg);
+ return true;
+ }
+}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeStressReplicationDomain.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeStressReplicationDomain.java
new file mode 100644
index 0000000..86f6263
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/FakeStressReplicationDomain.java
@@ -0,0 +1,159 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+import org.opends.server.types.ResultCode;
+import java.io.IOException;
+import java.util.concurrent.BlockingQueue;
+import org.opends.server.config.ConfigException;
+import java.util.Collection;
+import java.io.InputStream;
+import java.io.OutputStream;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.types.DirectoryException;
+import static org.opends.messages.ReplicationMessages.*;
+
+/**
+ * This class is the minimum implementation of a Concrete ReplicationDomain
+ * used to test the Generic Replication Service.
+ */
+public class FakeStressReplicationDomain extends ReplicationDomain
+{
+ // A blocking queue that is used to send the UpdateMsg received from
+ // the Replication Service.
+ BlockingQueue<UpdateMsg> queue = null;
+
+ // A string that will be exported should exportBackend be called.
+ String exportString = null;
+
+ // A StringBuilder that will be used to build a build a new String should the
+ // import be called.
+ StringBuilder importString = null;
+
+ public FakeStressReplicationDomain(
+ String serviceID,
+ short serverID,
+ Collection<String> replicationServers,
+ int window,
+ long heartbeatInterval,
+ BlockingQueue<UpdateMsg> queue) throws ConfigException
+ {
+ super(serviceID, serverID);
+ startPublishService(replicationServers, window, heartbeatInterval);
+ startListenService();
+ this.queue = queue;
+ }
+
+ public FakeStressReplicationDomain(
+ String serviceID,
+ short serverID,
+ Collection<String> replicationServers,
+ int window,
+ long heartbeatInterval,
+ String exportString,
+ StringBuilder importString) throws ConfigException
+ {
+ super(serviceID, serverID);
+ startPublishService(replicationServers, window, heartbeatInterval);
+ startListenService();
+ this.exportString = exportString;
+ this.importString = importString;
+ }
+
+ final int IMPORT_SIZE = 100000000;
+ @Override
+ public long countEntries() throws DirectoryException
+ {
+ return IMPORT_SIZE;
+ }
+
+ @Override
+ protected void exportBackend(OutputStream output) throws DirectoryException
+ {
+ System.out.println("export started");
+ try
+ {
+ for (int i=0; i< IMPORT_SIZE; i++)
+ {
+ output.write("this is a long key like a dn or something similar: value\n\n".getBytes());
+ }
+ output.flush();
+ output.close();
+ }
+ catch (IOException e)
+ {
+ throw new DirectoryException(ResultCode.OPERATIONS_ERROR,
+ ERR_BACKEND_EXPORT_ENTRY.get("", ""));
+ }
+ System.out.println("export finished");
+ }
+
+ @Override
+ public long getGenerationID()
+ {
+ return 1;
+ }
+
+ @Override
+ protected void importBackend(InputStream input) throws DirectoryException
+ {
+ long startDate = System.currentTimeMillis();
+ System.out.println("import started : " + startDate);
+
+ byte[] buffer = new byte[10000];
+ int ret;
+ int count = 0;
+ do
+ {
+ try
+ {
+ ret = input.read(buffer, 0, 10000);
+ } catch (IOException e)
+ {
+ e.printStackTrace();
+ throw new DirectoryException(
+ ResultCode.OPERATIONS_ERROR,
+ ERR_BACKEND_EXPORT_ENTRY.get("", ""));
+ }
+ count++;
+ }
+ while (ret >= 0);
+
+ long endDate = System.currentTimeMillis();
+ System.out.println("import end : " + endDate);
+ System.out.println("import duration (sec): " + (endDate - startDate) / 1000);
+ System.out.println("import count: " + count);
+ }
+
+ @Override
+ public boolean processUpdate(UpdateMsg updateMsg)
+ {
+ if (queue != null)
+ queue.add(updateMsg);
+ return true;
+ }
+}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java
new file mode 100644
index 0000000..1bac2f7
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/service/ReplicationDomainTest.java
@@ -0,0 +1,317 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.replication.service;
+
+import static org.testng.Assert.*;
+
+import java.util.TreeSet;
+
+import java.util.concurrent.LinkedBlockingQueue;
+
+import java.net.ServerSocket;
+import java.util.ArrayList;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import org.opends.server.TestCaseUtils;
+import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.common.DSInfo;
+import org.opends.server.replication.protocol.UpdateMsg;
+import org.opends.server.replication.server.ReplServerFakeConfiguration;
+import org.opends.server.replication.server.ReplicationServer;
+import org.testng.annotations.Test;
+
+/**
+ * Test the Generic Replication Service.
+ */
+public class ReplicationDomainTest extends ReplicationTestCase
+{
+ /**
+ * Test that a ReplicationDomain is able to publish and receive UpdateMsg.
+ */
+ @Test(enabled=true)
+ public void publishAndReceive() throws Exception
+ {
+ String testService = "test";
+ ReplicationServer replServer = null;
+ int replServerID = 10;
+ FakeReplicationDomain domain1 = null;
+ FakeReplicationDomain domain2 = null;
+
+ try
+ {
+ // find a free port for the replicationServer
+ ServerSocket socket = TestCaseUtils.bindFreePort();
+ int replServerPort = socket.getLocalPort();
+ socket.close();
+
+ ReplServerFakeConfiguration conf =
+ new ReplServerFakeConfiguration(
+ replServerPort, "ReplicationDomainTestDb",
+ 0, replServerID, 0, 100, null);
+
+ replServer = new ReplicationServer(conf);
+ ArrayList<String> servers = new ArrayList<String>(1);
+ servers.add("localhost:" + replServerPort);
+
+ BlockingQueue<UpdateMsg> rcvQueue1 = new LinkedBlockingQueue<UpdateMsg>();
+ domain1 = new FakeReplicationDomain(
+ testService, (short) 1, servers, 100, 1000, rcvQueue1);
+
+ BlockingQueue<UpdateMsg> rcvQueue2 = new LinkedBlockingQueue<UpdateMsg>();
+ domain2 = new FakeReplicationDomain(
+ testService, (short) 2, servers, 100, 1000, rcvQueue2);
+
+ /*
+ * Publish a message from domain1,
+ * Check that domain2 receives it shortly after.
+ */
+ byte[] test = {1, 2, 3 ,4, 0, 1, 2, 3, 4, 5};
+ domain1.publish(test);
+
+ UpdateMsg rcvdMsg = rcvQueue2.poll(1, TimeUnit.SECONDS);
+ assertNotNull(rcvdMsg);
+ assertEquals(test, rcvdMsg.getPayload());
+ }
+ finally
+ {
+ if (domain1 != null)
+ domain1.disableService();
+
+ if (domain2 != null)
+ domain2.disableService();
+
+ if (replServer != null)
+ replServer.remove();
+ }
+ }
+
+ /**
+ * Test that a ReplicationDomain is able to export and import its database.
+ */
+ @Test(enabled=true)
+ public void exportAndImport() throws Exception
+ {
+ final int ENTRYCOUNT=5000;
+ String testService = "test";
+ ReplicationServer replServer = null;
+ int replServerID = 11;
+ FakeReplicationDomain domain1 = null;
+ FakeReplicationDomain domain2 = null;
+
+ try
+ {
+ // find a free port for the replicationServer
+ ServerSocket socket = TestCaseUtils.bindFreePort();
+ int replServerPort = socket.getLocalPort();
+ socket.close();
+
+ ReplServerFakeConfiguration conf =
+ new ReplServerFakeConfiguration(
+ replServerPort, "ReplicationDomainTestDb",
+ 0, replServerID, 0, 100, null);
+
+ replServer = new ReplicationServer(conf);
+ ArrayList<String> servers = new ArrayList<String>(1);
+ servers.add("localhost:" + replServerPort);
+
+ StringBuilder exportedDataBuilder = new StringBuilder();
+ for (int i =0; i<ENTRYCOUNT; i++)
+ {
+ exportedDataBuilder.append("key : value"+i+"\n\n");
+ }
+ String exportedData=exportedDataBuilder.toString();
+ domain1 = new FakeReplicationDomain(
+ testService, (short) 1, servers,
+ 100, 0, exportedData, null, ENTRYCOUNT);
+
+ StringBuilder importedData = new StringBuilder();
+ domain2 = new FakeReplicationDomain(
+ testService, (short) 2, servers, 100, 0,
+ null, importedData, 0);
+
+ /*
+ * Trigger a total update from domain1 to domain2.
+ * Check that the exported data is correctly received on domain2.
+ */
+ for (DSInfo remoteDS : domain2.getDsList())
+ {
+ if (remoteDS.getDsId() != domain2.getServerId())
+ {
+ domain2.initializeFromRemote(remoteDS.getDsId() , null);
+ break;
+ }
+ }
+
+ int count = 0;
+ while ((importedData.length() < exportedData.length()) && (count < 500))
+ {
+ count ++;
+ Thread.sleep(100);
+ }
+ assertTrue(domain2.getLeftEntryCount() == 0,
+ "LeftEntryCount for export is " + domain2.getLeftEntryCount());
+ assertTrue(domain1.getLeftEntryCount() == 0,
+ "LeftEntryCount for import is " + domain1.getLeftEntryCount());
+ assertEquals(importedData.length(), exportedData.length());
+ assertEquals(importedData.toString(), exportedData);
+ }
+ finally
+ {
+ if (domain1 != null)
+ domain1.disableService();
+
+ if (domain2 != null)
+ domain2.disableService();
+
+ if (replServer != null)
+ replServer.remove();
+ }
+ }
+
+ /**
+ * Sender side of the Total Update Perf test.
+ * The goal of this test is to measure the performance
+ * of the total update code.
+ * It is not intended to be run as part of the daily unit test but
+ * should only be used manually by developer in need of testing the
+ * performance improvement or non-regression of the total update code.
+ * Use this test in combination with the receiverInitialize() :
+ * - enable the test
+ * - start the senderInitialize first using
+ * ./build.sh \
+ * -Dorg.opends.test.suppressOutput=false \
+ * -Dtest.methods=org.opends.server.replication.service.ReplicationDomainTest.senderInitialize test
+ * - start the receiverInitialize second.
+ * - you may want to change HOST1 and HOST2 to use 2 different hosts
+ * if you don't want to do a loopback test.
+ * - don't forget to disable again the tests after running them
+ */
+ final String HOST1 = "localhost:";
+ final String HOST2 = "localhost:";
+ final int SENDERPORT = 10102;
+ final int RECEIVERPORT = 10101;
+
+ @Test(enabled=false)
+ public void senderInitialize() throws Exception
+ {
+ String testService = "test";
+ ReplicationServer replServer = null;
+ int replServerID = 12;
+ FakeStressReplicationDomain domain1 = null;
+
+ try
+ {
+ TreeSet<String> servers = new TreeSet<String>();
+ servers.add(HOST1 + SENDERPORT);
+ servers.add(HOST2 + RECEIVERPORT);
+
+ ReplServerFakeConfiguration conf =
+ new ReplServerFakeConfiguration(
+ SENDERPORT, "ReplicationDomainTestDb",
+ 0, replServerID, 0, 100, servers);
+
+ replServer = new ReplicationServer(conf);
+
+ BlockingQueue<UpdateMsg> rcvQueue1 = new LinkedBlockingQueue<UpdateMsg>();
+ domain1 = new FakeStressReplicationDomain(
+ testService, (short) 2, servers, 100, 1000, rcvQueue1);
+
+ System.out.println("waiting");
+ Thread.sleep(1000000000);
+ }
+ finally
+ {
+ if (domain1 != null)
+ domain1.disableService();
+
+ if (replServer != null)
+ replServer.remove();
+ }
+ }
+
+ /**
+ * See comments in senderInitialize() above
+ */
+ @Test(enabled=false)
+ public void receiverInitialize() throws Exception
+ {
+ String testService = "test";
+ ReplicationServer replServer = null;
+ int replServerID = 11;
+ FakeStressReplicationDomain domain1 = null;
+
+ try
+ {
+ TreeSet<String> servers = new TreeSet<String>();
+ servers.add(HOST1 + SENDERPORT);
+ servers.add(HOST2 + RECEIVERPORT);
+
+ ReplServerFakeConfiguration conf =
+ new ReplServerFakeConfiguration(
+ RECEIVERPORT, "ReplicationDomainTestDb",
+ 0, replServerID, 0, 100, servers);
+
+ replServer = new ReplicationServer(conf);
+
+ BlockingQueue<UpdateMsg> rcvQueue1 = new LinkedBlockingQueue<UpdateMsg>();
+ domain1 = new FakeStressReplicationDomain(
+ testService, (short) 1, servers, 100, 100000, rcvQueue1);
+ /*
+ * Trigger a total update from domain1 to domain2.
+ * Check that the exported data is correctly received on domain2.
+ */
+ boolean alone = true;
+ while (alone)
+ {
+ for (DSInfo remoteDS : domain1.getDsList())
+ {
+ if (remoteDS.getDsId() != domain1.getServerId())
+ {
+ alone = false;
+ domain1.initializeFromRemote(remoteDS.getDsId() , null);
+ break;
+ }
+ }
+ if (alone)
+ {
+ System.out.println("trying...");
+ Thread.sleep(1000);
+ }
+ }
+ System.out.println("waiting");
+ Thread.sleep(10000000);
+ }
+ finally
+ {
+ if (domain1 != null)
+ domain1.disableService();
+
+ if (replServer != null)
+ replServer.remove();
+ }
+ }
+}
--
Gitblit v1.10.0