You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ratis.apache.org by ji...@apache.org on 2017/09/04 21:55:49 UTC

[1/2] incubator-ratis git commit: RATIS-100. Fix bugs for running multiple raft groups with a state machine. Contributed by Tsz Wo Nicholas Sze.

Repository: incubator-ratis
Updated Branches:
  refs/heads/master c2032801a -> 9ce1783d3


RATIS-100. Fix bugs for running multiple raft groups with a state machine. Contributed by Tsz Wo Nicholas Sze.


Project: http://git-wip-us.apache.org/repos/asf/incubator-ratis/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ratis/commit/4ed8f727
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ratis/tree/4ed8f727
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ratis/diff/4ed8f727

Branch: refs/heads/master
Commit: 4ed8f72716a931835a28dbd62ec5898a97d99450
Parents: c203280
Author: Jing Zhao <ji...@apache.org>
Authored: Mon Sep 4 14:54:15 2017 -0700
Committer: Jing Zhao <ji...@apache.org>
Committed: Mon Sep 4 14:54:15 2017 -0700

----------------------------------------------------------------------
 .../ratis/protocol/GroupMismatchException.java  | 28 ++++++++
 .../org/apache/ratis/TestMultiRaftGroup.java    | 76 ++++++++++++++++++++
 .../ratis/server/impl/PendingRequests.java      |  7 +-
 .../ratis/server/impl/RaftServerImpl.java       | 12 ++++
 .../ratis/server/impl/StateMachineUpdater.java  |  4 ++
 .../java/org/apache/ratis/MiniRaftCluster.java  |  4 ++
 .../java/org/apache/ratis/RaftBasicTests.java   |  4 +-
 .../server/impl/ReinitializationBaseTest.java   | 36 +++++++---
 .../ratis/statemachine/TestStateMachine.java    |  2 +-
 9 files changed, 157 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/4ed8f727/ratis-common/src/main/java/org/apache/ratis/protocol/GroupMismatchException.java
----------------------------------------------------------------------
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/GroupMismatchException.java b/ratis-common/src/main/java/org/apache/ratis/protocol/GroupMismatchException.java
new file mode 100644
index 0000000..af60825
--- /dev/null
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/GroupMismatchException.java
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.protocol;
+
+/**
+ * This exception indicates that the group id in the request does not match
+ * server's group id.
+ */
+public class GroupMismatchException extends RaftException {
+  public GroupMismatchException(String message) {
+    super(message);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/4ed8f727/ratis-examples/src/test/java/org/apache/ratis/TestMultiRaftGroup.java
----------------------------------------------------------------------
diff --git a/ratis-examples/src/test/java/org/apache/ratis/TestMultiRaftGroup.java b/ratis-examples/src/test/java/org/apache/ratis/TestMultiRaftGroup.java
new file mode 100644
index 0000000..8d3966e
--- /dev/null
+++ b/ratis-examples/src/test/java/org/apache/ratis/TestMultiRaftGroup.java
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis;
+
+
+import org.apache.log4j.Level;
+import org.apache.ratis.client.RaftClient;
+import org.apache.ratis.examples.RaftExamplesTestUtil;
+import org.apache.ratis.examples.arithmetic.ArithmeticStateMachine;
+import org.apache.ratis.examples.arithmetic.TestArithmetic;
+import org.apache.ratis.protocol.RaftGroup;
+import org.apache.ratis.server.impl.RaftServerImpl;
+import org.apache.ratis.server.impl.ReinitializationBaseTest;
+import org.apache.ratis.util.CheckedBiConsumer;
+import org.apache.ratis.util.LogUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@RunWith(Parameterized.class)
+public class TestMultiRaftGroup extends BaseTest {
+  static {
+    LogUtils.setLogLevel(RaftServerImpl.LOG, Level.TRACE);
+  }
+
+  @Parameterized.Parameters
+  public static Collection<Object[]> data() throws IOException {
+    return RaftExamplesTestUtil.getMiniRaftClusters(ArithmeticStateMachine.class, 0);
+  }
+
+  @Parameterized.Parameter
+  public MiniRaftCluster cluster;
+
+  @Test
+  public void testMultiRaftGroup() throws Exception {
+    runTestMultiRaftGroup(3, 6, 9, 12, 15);
+  }
+
+  private void runTestMultiRaftGroup(int... idIndex) throws Exception {
+    runTestMultiRaftGroup(idIndex, -1);
+  }
+
+  private final AtomicInteger start = new AtomicInteger(3);
+  private final int count = 10;
+
+  private void runTestMultiRaftGroup(int[] idIndex, int chosen) throws Exception {
+
+    final CheckedBiConsumer<MiniRaftCluster, RaftGroup, IOException> checker = (cluster, group) -> {
+      try (final RaftClient client = cluster.createClient(group)) {
+        TestArithmetic.runTestPythagorean(client, start.getAndAdd(2*count), count);
+      }
+    };
+
+    ReinitializationBaseTest.runTestReinitializeMultiGroups(
+        cluster, idIndex, chosen, checker);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/4ed8f727/ratis-server/src/main/java/org/apache/ratis/server/impl/PendingRequests.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/PendingRequests.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/PendingRequests.java
index 98bc0a7..b7b8a9e 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/PendingRequests.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/PendingRequests.java
@@ -24,7 +24,6 @@ import org.slf4j.Logger;
 
 import java.io.IOException;
 import java.util.Collection;
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.stream.Collectors;
@@ -45,7 +44,10 @@ class PendingRequests {
       TransactionContext entry) {
     // externally synced for now
     Preconditions.assertTrue(!request.isReadOnly());
-    Preconditions.assertTrue(last == null || index == last.getIndex() + 1);
+    if (last != null && !(last.getRequest() instanceof SetConfigurationRequest)) {
+      Preconditions.assertTrue(index == last.getIndex() + 1,
+          () -> "index = " + index + " != last.getIndex() + 1, last=" + last);
+    }
     return add(index, request, entry);
   }
 
@@ -60,6 +62,7 @@ class PendingRequests {
   PendingRequest addConfRequest(SetConfigurationRequest request) {
     Preconditions.assertTrue(pendingSetConf == null);
     pendingSetConf = new PendingRequest(request);
+    last = pendingSetConf;
     return pendingSetConf;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/4ed8f727/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
index 14acfb4..4463914 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
@@ -398,6 +398,14 @@ public class RaftServerImpl implements RaftServerProtocol,
         expected);
   }
 
+  void assertGroup(Object requestorId, RaftGroupId requestorGroupId) throws GroupMismatchException {
+    if (!groupId.equals(requestorGroupId)) {
+      throw new GroupMismatchException(getId()
+          + ": The group (" + requestorGroupId + ") of requestor " + requestorId
+          + " does not match the group (" + groupId + ") of the server " + getId());
+    }
+  }
+
   /**
    * Handle a normal update request from client.
    */
@@ -595,6 +603,7 @@ public class RaftServerImpl implements RaftServerProtocol,
     LOG.debug("{}: receive requestVote({}, {}, {}, {})",
         getId(), candidateId, candidateGroupId, candidateTerm, candidateLastEntry);
     assertLifeCycleState(RUNNING);
+    assertGroup(candidateId, candidateGroupId);
 
     boolean voteGranted = false;
     boolean shouldShutdown = false;
@@ -699,6 +708,7 @@ public class RaftServerImpl implements RaftServerProtocol,
             + initializing + ServerProtoUtils.toString(entries));
 
     assertLifeCycleState(STARTING, RUNNING);
+    assertGroup(leaderId, leaderGroupId);
 
     try {
       validateEntries(leaderTerm, previous, entries);
@@ -792,11 +802,13 @@ public class RaftServerImpl implements RaftServerProtocol,
       InstallSnapshotRequestProto request) throws IOException {
     final RaftRpcRequestProto r = request.getServerRequest();
     final RaftPeerId leaderId = RaftPeerId.valueOf(r.getRequestorId());
+    final RaftGroupId leaderGroupId = ProtoUtils.toRaftGroupId(r.getRaftGroupId());
     CodeInjectionForTesting.execute(INSTALL_SNAPSHOT, getId(),
         leaderId, request);
     LOG.debug("{}: receive installSnapshot({})", getId(), request);
 
     assertLifeCycleState(STARTING, RUNNING);
+    assertGroup(leaderId, leaderGroupId);
 
     final long currentTerm;
     final long leaderTerm = request.getLeaderTerm();

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/4ed8f727/ratis-server/src/main/java/org/apache/ratis/server/impl/StateMachineUpdater.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/StateMachineUpdater.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/StateMachineUpdater.java
index 7ed6731..9ef6ce7 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/StateMachineUpdater.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/StateMachineUpdater.java
@@ -86,6 +86,10 @@ class StateMachineUpdater implements Runnable {
     state = State.STOP;
     updater.interrupt();
     try {
+      updater.join();
+    } catch (InterruptedException ignored) {
+    }
+    try {
       stateMachine.close();
     } catch (IOException ignored) {
     }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/4ed8f727/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java b/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java
index b527a58..79cb9bb 100644
--- a/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java
+++ b/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java
@@ -422,6 +422,10 @@ public abstract class MiniRaftCluster {
     return createClient(null, group);
   }
 
+  public RaftClient createClient(RaftGroup g) {
+    return createClient(null, g);
+  }
+
   public RaftClient createClient(RaftPeerId leaderId) {
     return createClient(leaderId, group);
   }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/4ed8f727/ratis-server/src/test/java/org/apache/ratis/RaftBasicTests.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/RaftBasicTests.java b/ratis-server/src/test/java/org/apache/ratis/RaftBasicTests.java
index b227e47..7e08809 100644
--- a/ratis-server/src/test/java/org/apache/ratis/RaftBasicTests.java
+++ b/ratis-server/src/test/java/org/apache/ratis/RaftBasicTests.java
@@ -80,7 +80,7 @@ public abstract class RaftBasicTests extends BaseTest {
     LOG.info(cluster.printServers());
 
     final SimpleMessage[] messages = SimpleMessage.create(10);
-    try(final RaftClient client = cluster.createClient(null)) {
+    try(final RaftClient client = cluster.createClient()) {
       for (SimpleMessage message : messages) {
         client.send(message);
       }
@@ -149,7 +149,7 @@ public abstract class RaftBasicTests extends BaseTest {
 
     final List<Client4TestWithLoad> clients
         = Stream.iterate(0, i -> i+1).limit(numClients)
-        .map(i -> cluster.createClient(null))
+        .map(i -> cluster.createClient())
         .map(c -> new Client4TestWithLoad(c, numMessages))
         .collect(Collectors.toList());
     clients.forEach(Thread::start);

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/4ed8f727/ratis-server/src/test/java/org/apache/ratis/server/impl/ReinitializationBaseTest.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/server/impl/ReinitializationBaseTest.java b/ratis-server/src/test/java/org/apache/ratis/server/impl/ReinitializationBaseTest.java
index 5bc8dbe..d9068d1 100644
--- a/ratis-server/src/test/java/org/apache/ratis/server/impl/ReinitializationBaseTest.java
+++ b/ratis-server/src/test/java/org/apache/ratis/server/impl/ReinitializationBaseTest.java
@@ -27,10 +27,13 @@ import org.apache.ratis.protocol.RaftGroup;
 import org.apache.ratis.protocol.RaftGroupId;
 import org.apache.ratis.protocol.RaftPeer;
 import org.apache.ratis.protocol.RaftPeerId;
+import org.apache.ratis.util.CheckedBiConsumer;
 import org.apache.ratis.util.JavaUtils;
 import org.apache.ratis.util.LogUtils;
 import org.junit.Assert;
 import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.Arrays;
@@ -42,7 +45,9 @@ import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 public abstract class ReinitializationBaseTest extends BaseTest {
-  static {
+  static final Logger LOG = LoggerFactory.getLogger(ReinitializationBaseTest.class);
+
+  {
     LogUtils.setLogLevel(RaftServerImpl.LOG, Level.DEBUG);
     LogUtils.setLogLevel(RaftClient.LOG, Level.DEBUG);
   }
@@ -78,7 +83,7 @@ public abstract class ReinitializationBaseTest extends BaseTest {
 
     // Reinitialize servers
     final RaftGroup newGroup = new RaftGroup(groupId, cluster.getPeers());
-    final RaftClient client = cluster.createClient(null, newGroup);
+    final RaftClient client = cluster.createClient(newGroup);
     for(RaftPeer p : newGroup.getPeers()) {
       client.reinitialize(newGroup, p.getId());
     }
@@ -106,8 +111,15 @@ public abstract class ReinitializationBaseTest extends BaseTest {
 
   private void runTestReinitializeMultiGroups(int[] idIndex, int chosen) throws Exception {
     printThreadCount(null, "init");
-    final MiniRaftCluster cluster = getCluster(0);
+    runTestReinitializeMultiGroups(getCluster(0), idIndex, chosen, NOOP);
+  }
+
+  static final CheckedBiConsumer<MiniRaftCluster, RaftGroup, RuntimeException> NOOP = (c, g) -> {};
 
+  public static <T extends Throwable> void runTestReinitializeMultiGroups(
+      MiniRaftCluster cluster, int[] idIndex, int chosen,
+      CheckedBiConsumer<MiniRaftCluster, RaftGroup, T> checker)
+      throws IOException, InterruptedException, T {
     if (chosen < 0) {
       chosen = ThreadLocalRandom.current().nextInt(idIndex.length);
     }
@@ -147,6 +159,7 @@ public abstract class ReinitializationBaseTest extends BaseTest {
         }
       }
       Assert.assertNotNull(RaftTestUtil.waitForLeader(cluster, true, gid));
+      checker.accept(cluster, groups[i]);
     }
     printThreadCount(type, "start groups");
     LOG.info("start groups: " + cluster.printServers());
@@ -171,22 +184,23 @@ public abstract class ReinitializationBaseTest extends BaseTest {
 
     // update chosen group to use all the peers
     final RaftGroup newGroup = new RaftGroup(groups[chosen].getGroupId());
-    final RaftPeer[] array = allPeers.toArray(RaftPeer.EMPTY_PEERS);
     for(int i = 0; i < groups.length; i++) {
-      LOG.info(i + ") update " + cluster.printServers(groups[i].getGroupId()));
-      if (i == chosen) {
-        try (final RaftClient client = cluster.createClient(null, groups[i])) {
-          client.setConfiguration(array);
-        }
-      } else {
-        for(RaftPeer p : groups[i].getPeers()) {
+      if (i != chosen) {
+        LOG.info(i + ") reinitialize: " + cluster.printServers(groups[i].getGroupId()));
+        for (RaftPeer p : groups[i].getPeers()) {
           try (final RaftClient client = cluster.createClient(p.getId(), groups[i])) {
             client.reinitialize(newGroup, p.getId());
           }
         }
       }
     }
+    LOG.info(chosen + ") setConfiguration: " + cluster.printServers(groups[chosen].getGroupId()));
+    try (final RaftClient client = cluster.createClient(groups[chosen])) {
+      client.setConfiguration(allPeers.toArray(RaftPeer.EMPTY_PEERS));
+    }
+
     Assert.assertNotNull(RaftTestUtil.waitForLeader(cluster, true));
+    checker.accept(cluster, groups[chosen]);
     LOG.info("update groups: " + cluster.printServers());
     printThreadCount(type, "update groups");
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/4ed8f727/ratis-server/src/test/java/org/apache/ratis/statemachine/TestStateMachine.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/statemachine/TestStateMachine.java b/ratis-server/src/test/java/org/apache/ratis/statemachine/TestStateMachine.java
index f41e764..73ce69d 100644
--- a/ratis-server/src/test/java/org/apache/ratis/statemachine/TestStateMachine.java
+++ b/ratis-server/src/test/java/org/apache/ratis/statemachine/TestStateMachine.java
@@ -156,7 +156,7 @@ public class TestStateMachine extends BaseTest {
 
     int numTrx = 100;
     final RaftTestUtil.SimpleMessage[] messages = RaftTestUtil.SimpleMessage.create(numTrx);
-    try(final RaftClient client = cluster.createClient(null)) {
+    try(final RaftClient client = cluster.createClient()) {
       for (RaftTestUtil.SimpleMessage message : messages) {
         client.send(message);
       }


[2/2] incubator-ratis git commit: RATIS-105. Server should check group id for client requests. Contributed by Tsz Wo Nicholas Sze.

Posted by ji...@apache.org.
RATIS-105. Server should check group id for client requests. Contributed by Tsz Wo Nicholas Sze.


Project: http://git-wip-us.apache.org/repos/asf/incubator-ratis/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ratis/commit/9ce1783d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ratis/tree/9ce1783d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ratis/diff/9ce1783d

Branch: refs/heads/master
Commit: 9ce1783d30726b940193e1926659fefdbe2a2cdb
Parents: 4ed8f72
Author: Jing Zhao <ji...@apache.org>
Authored: Mon Sep 4 14:54:42 2017 -0700
Committer: Jing Zhao <ji...@apache.org>
Committed: Mon Sep 4 14:54:42 2017 -0700

----------------------------------------------------------------------
 .../ratis/client/impl/ClientProtoUtils.java     |   4 +-
 .../ratis/client/impl/RaftClientImpl.java       |  18 +-
 .../ratis/protocol/NotLeaderException.java      |   2 +-
 .../org/apache/ratis/protocol/RaftPeer.java     |   7 +-
 .../ratis/protocol/StateMachineException.java   |   2 +-
 .../test/java/org/apache/ratis/BaseTest.java    |  19 ++
 .../ratis/grpc/MiniRaftClusterWithGRpc.java     |   7 +
 .../grpc/TestNotLeaderExceptionWithGrpc.java    |  31 ---
 .../ratis/grpc/TestRaftExceptionWithGrpc.java   |  25 +++
 .../ratis/hadooprpc/client/HadoopClientRpc.java |   3 +-
 .../hadooprpc/MiniRaftClusterWithHadoopRpc.java |   7 +
 .../TestNotLeaderExceptionWithHadoopRpc.java    |  31 ---
 .../TestRaftExceptionWithHadoopRpc.java         |  25 +++
 .../ratis/netty/MiniRaftClusterWithNetty.java   |   7 +
 .../netty/TestNotLeaderExceptionWithNetty.java  |  31 ---
 .../ratis/netty/TestRaftExceptionWithNetty.java |  25 +++
 .../ratis/server/impl/RaftServerImpl.java       |  29 ++-
 .../ratis/server/impl/RaftServerProxy.java      |   2 +
 .../apache/ratis/server/impl/RetryCache.java    |  25 ++-
 .../apache/ratis/server/storage/RaftLog.java    |   2 +-
 .../java/org/apache/ratis/MiniRaftCluster.java  |   4 +
 .../org/apache/ratis/RaftExceptionBaseTest.java | 199 +++++++++++++++++++
 .../ratis/RaftNotLeaderExceptionBaseTest.java   | 170 ----------------
 .../server/impl/ReinitializationBaseTest.java   |   4 +-
 .../MiniRaftClusterWithSimulatedRpc.java        |   7 +
 .../TestNotLeaderExceptionWithSimulation.java   |  28 ---
 .../TestRaftExceptionWithSimulation.java        |  25 +++
 27 files changed, 415 insertions(+), 324 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientProtoUtils.java
----------------------------------------------------------------------
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientProtoUtils.java b/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientProtoUtils.java
index e6621de..ea8c5ac 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientProtoUtils.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientProtoUtils.java
@@ -144,7 +144,7 @@ public class ClientProtoUtils {
           suggestedLeader, peers);
     } else if (replyProto.getExceptionDetailsCase().equals(STATEMACHINEEXCEPTION)) {
       StateMachineExceptionProto smeProto = replyProto.getStateMachineException();
-      e = wrapStateMachineException(rp.getReplyId().toStringUtf8(),
+      e = wrapStateMachineException(RaftPeerId.valueOf(rp.getReplyId()),
           smeProto.getExceptionClassName(), smeProto.getErrorMsg(),
           smeProto.getStacktrace());
     }
@@ -156,7 +156,7 @@ public class ClientProtoUtils {
   }
 
   private static StateMachineException wrapStateMachineException(
-      String serverId, String className, String errorMsg,
+      RaftPeerId serverId, String className, String errorMsg,
       ByteString stackTraceBytes) {
     StateMachineException sme;
     if (className == null) {

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
----------------------------------------------------------------------
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java b/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
index 69483ef..c25b9e0 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
@@ -27,10 +27,7 @@ import org.apache.ratis.protocol.*;
 
 import java.io.IOException;
 import java.io.InterruptedIOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
@@ -82,6 +79,8 @@ final class RaftClientImpl implements RaftClient {
   }
 
   private RaftClientReply send(Message message, boolean readOnly) throws IOException {
+    Objects.requireNonNull(message, "message == null");
+
     final long callId = nextCallId();
     return sendRequestWithRetry(() -> new RaftClientRequest(
         clientId, leaderId, groupId, callId, message, readOnly));
@@ -90,6 +89,8 @@ final class RaftClientImpl implements RaftClient {
   @Override
   public RaftClientReply setConfiguration(RaftPeer[] peersInNewConf)
       throws IOException {
+    Objects.requireNonNull(peersInNewConf, "peersInNewConf == null");
+
     final long callId = nextCallId();
     // also refresh the rpc proxies for these peers
     addServers(Arrays.stream(peersInNewConf));
@@ -100,6 +101,9 @@ final class RaftClientImpl implements RaftClient {
   @Override
   public RaftClientReply reinitialize(RaftGroup newGroup, RaftPeerId server)
       throws IOException {
+    Objects.requireNonNull(newGroup, "newGroup == null");
+    Objects.requireNonNull(server, "server == null");
+
     final long callId = nextCallId();
     addServers(newGroup.getPeers().stream());
     return sendRequest(new ReinitializeRequest(
@@ -113,7 +117,7 @@ final class RaftClientImpl implements RaftClient {
 
   private RaftClientReply sendRequestWithRetry(
       Supplier<RaftClientRequest> supplier)
-      throws InterruptedIOException, StateMachineException {
+      throws InterruptedIOException, StateMachineException, GroupMismatchException {
     for(;;) {
       final RaftClientRequest request = supplier.get();
       final RaftClientReply reply = sendRequest(request);
@@ -133,11 +137,13 @@ final class RaftClientImpl implements RaftClient {
   }
 
   private RaftClientReply sendRequest(RaftClientRequest request)
-      throws StateMachineException {
+      throws StateMachineException, GroupMismatchException {
     LOG.debug("{}: {}", clientId, request);
     RaftClientReply reply = null;
     try {
       reply = clientRpc.sendRequest(request);
+    } catch (GroupMismatchException gme) {
+      throw gme;
     } catch (IOException ioe) {
       handleIOException(request, ioe, null);
     }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-common/src/main/java/org/apache/ratis/protocol/NotLeaderException.java
----------------------------------------------------------------------
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/NotLeaderException.java b/ratis-common/src/main/java/org/apache/ratis/protocol/NotLeaderException.java
index 062f5de..453965e 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/NotLeaderException.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/NotLeaderException.java
@@ -27,7 +27,7 @@ public class NotLeaderException extends RaftException {
     super("Server " + id + " is not the leader (" + suggestedLeader
         + "). Request must be sent to leader.");
     this.suggestedLeader = suggestedLeader;
-    this.peers = peers == null ? RaftPeer.EMPTY_PEERS : peers;
+    this.peers = peers == null ? RaftPeer.emptyArray(): peers;
   }
 
   public RaftPeer getSuggestedLeader() {

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeer.java
----------------------------------------------------------------------
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeer.java b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeer.java
index 26c9c4c..68252f3 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeer.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeer.java
@@ -28,7 +28,12 @@ import java.util.Objects;
  * The objects of this class are immutable.
  */
 public class RaftPeer {
-  public static final RaftPeer[] EMPTY_PEERS = {};
+  private static final RaftPeer[] EMPTY_ARRAY = {};
+
+  /** @return an empty array. */
+  public static RaftPeer[] emptyArray() {
+    return EMPTY_ARRAY;
+  }
 
   /** The id of the peer. */
   private final RaftPeerId id;

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-common/src/main/java/org/apache/ratis/protocol/StateMachineException.java
----------------------------------------------------------------------
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/StateMachineException.java b/ratis-common/src/main/java/org/apache/ratis/protocol/StateMachineException.java
index 68d808b..49a64ef 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/StateMachineException.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/StateMachineException.java
@@ -18,7 +18,7 @@
 package org.apache.ratis.protocol;
 
 public class StateMachineException extends RaftException {
-  public StateMachineException(String serverId, Throwable cause) {
+  public StateMachineException(RaftPeerId serverId, Throwable cause) {
     super(cause.getClass().getName() + " from Server " + serverId, cause);
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-common/src/test/java/org/apache/ratis/BaseTest.java
----------------------------------------------------------------------
diff --git a/ratis-common/src/test/java/org/apache/ratis/BaseTest.java b/ratis-common/src/test/java/org/apache/ratis/BaseTest.java
index 659da0b..f77cc64 100644
--- a/ratis-common/src/test/java/org/apache/ratis/BaseTest.java
+++ b/ratis-common/src/test/java/org/apache/ratis/BaseTest.java
@@ -19,8 +19,10 @@ package org.apache.ratis;
 
 import org.apache.log4j.Level;
 import org.apache.ratis.conf.ConfUtils;
+import org.apache.ratis.util.CheckedRunnable;
 import org.apache.ratis.util.JavaUtils;
 import org.apache.ratis.util.LogUtils;
+import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.rules.Timeout;
 import org.slf4j.Logger;
@@ -69,4 +71,21 @@ public abstract class BaseTest {
   public File getTestDir() {
     return getClassTestDir(getClass());
   }
+
+  public static void testFailureCase(
+      String description, CheckedRunnable<?> testCode,
+      Class<? extends Throwable> exceptedThrowableClass, Logger log) {
+    try {
+      testCode.run();
+      Assert.fail("The test \"" + description + "\" does not throw anything.");
+    } catch (Throwable t) {
+      log.info("The test \"" + description + "\" throws " + t.getClass().getSimpleName(), t);
+      Assert.assertEquals(exceptedThrowableClass, t.getClass());
+    }
+  }
+
+  public void testFailureCase(
+      String description, CheckedRunnable<?> testCode, Class<? extends Throwable> exceptedThrowableClass) {
+    testFailureCase(description, testCode, exceptedThrowableClass, LOG);
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGRpc.java
----------------------------------------------------------------------
diff --git a/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGRpc.java b/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGRpc.java
index a5992b1..3580f22 100644
--- a/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGRpc.java
+++ b/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGRpc.java
@@ -41,6 +41,13 @@ public class MiniRaftClusterWithGRpc extends MiniRaftCluster.RpcBase {
     }
   };
 
+  public interface FactoryGet extends Factory.Get<MiniRaftClusterWithGRpc> {
+    @Override
+    default Factory<MiniRaftClusterWithGRpc> getFactory() {
+      return FACTORY;
+    }
+  }
+
   public static final DelayLocalExecutionInjection sendServerRequestInjection =
       new DelayLocalExecutionInjection(RaftGRpcService.GRPC_SEND_SERVER_REQUEST);
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestNotLeaderExceptionWithGrpc.java
----------------------------------------------------------------------
diff --git a/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestNotLeaderExceptionWithGrpc.java b/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestNotLeaderExceptionWithGrpc.java
deleted file mode 100644
index e9e20b0..0000000
--- a/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestNotLeaderExceptionWithGrpc.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.ratis.grpc;
-
-import org.apache.ratis.MiniRaftCluster;
-import org.apache.ratis.RaftNotLeaderExceptionBaseTest;
-import org.apache.ratis.conf.RaftProperties;
-
-import java.io.IOException;
-
-public class TestNotLeaderExceptionWithGrpc extends RaftNotLeaderExceptionBaseTest {
-  @Override
-  public MiniRaftCluster.Factory<?> getFactory() {
-    return MiniRaftClusterWithGRpc.FACTORY;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestRaftExceptionWithGrpc.java
----------------------------------------------------------------------
diff --git a/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestRaftExceptionWithGrpc.java b/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestRaftExceptionWithGrpc.java
new file mode 100644
index 0000000..fc110ea
--- /dev/null
+++ b/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestRaftExceptionWithGrpc.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.grpc;
+
+import org.apache.ratis.RaftExceptionBaseTest;
+
+public class TestRaftExceptionWithGrpc
+    extends RaftExceptionBaseTest<MiniRaftClusterWithGRpc>
+    implements MiniRaftClusterWithGRpc.FactoryGet {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/HadoopClientRpc.java
----------------------------------------------------------------------
diff --git a/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/HadoopClientRpc.java b/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/HadoopClientRpc.java
index 3a2d6fc..c5a6ba9 100644
--- a/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/HadoopClientRpc.java
+++ b/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/HadoopClientRpc.java
@@ -54,7 +54,8 @@ public class HadoopClientRpc implements RaftClientRpc {
           ReconfigurationTimeoutException.class,
           ReconfigurationInProgressException.class,
           RaftException.class,
-          LeaderNotReadyException.class);
+          LeaderNotReadyException.class,
+          GroupMismatchException.class);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/MiniRaftClusterWithHadoopRpc.java
----------------------------------------------------------------------
diff --git a/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/MiniRaftClusterWithHadoopRpc.java b/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/MiniRaftClusterWithHadoopRpc.java
index bc420f4..51b1d5d 100644
--- a/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/MiniRaftClusterWithHadoopRpc.java
+++ b/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/MiniRaftClusterWithHadoopRpc.java
@@ -39,6 +39,13 @@ public class MiniRaftClusterWithHadoopRpc extends MiniRaftCluster.RpcBase {
   static final Logger LOG = LoggerFactory.getLogger(MiniRaftClusterWithHadoopRpc.class);
 
   public static class Factory extends MiniRaftCluster.Factory<MiniRaftClusterWithHadoopRpc> {
+    public interface Get extends MiniRaftCluster.Factory.Get<MiniRaftClusterWithHadoopRpc> {
+      @Override
+      default MiniRaftCluster.Factory<MiniRaftClusterWithHadoopRpc> getFactory() {
+        return FACTORY;
+      }
+    }
+
     @Override
     public MiniRaftClusterWithHadoopRpc newCluster(String[] ids, RaftProperties prop) {
       final Configuration conf = new Configuration();

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestNotLeaderExceptionWithHadoopRpc.java
----------------------------------------------------------------------
diff --git a/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestNotLeaderExceptionWithHadoopRpc.java b/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestNotLeaderExceptionWithHadoopRpc.java
deleted file mode 100644
index 103a0b3..0000000
--- a/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestNotLeaderExceptionWithHadoopRpc.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.ratis.hadooprpc;
-
-import org.apache.ratis.MiniRaftCluster;
-import org.apache.ratis.RaftNotLeaderExceptionBaseTest;
-import org.apache.ratis.conf.RaftProperties;
-
-import java.io.IOException;
-
-public class TestNotLeaderExceptionWithHadoopRpc extends RaftNotLeaderExceptionBaseTest {
-  @Override
-  public MiniRaftCluster.Factory<?> getFactory() {
-    return MiniRaftClusterWithHadoopRpc.FACTORY;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestRaftExceptionWithHadoopRpc.java
----------------------------------------------------------------------
diff --git a/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestRaftExceptionWithHadoopRpc.java b/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestRaftExceptionWithHadoopRpc.java
new file mode 100644
index 0000000..c60e183
--- /dev/null
+++ b/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestRaftExceptionWithHadoopRpc.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.hadooprpc;
+
+import org.apache.ratis.RaftExceptionBaseTest;
+
+public class TestRaftExceptionWithHadoopRpc
+    extends RaftExceptionBaseTest<MiniRaftClusterWithHadoopRpc>
+    implements MiniRaftClusterWithHadoopRpc.Factory.Get {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-netty/src/test/java/org/apache/ratis/netty/MiniRaftClusterWithNetty.java
----------------------------------------------------------------------
diff --git a/ratis-netty/src/test/java/org/apache/ratis/netty/MiniRaftClusterWithNetty.java b/ratis-netty/src/test/java/org/apache/ratis/netty/MiniRaftClusterWithNetty.java
index e6b19f8..3941f33 100644
--- a/ratis-netty/src/test/java/org/apache/ratis/netty/MiniRaftClusterWithNetty.java
+++ b/ratis-netty/src/test/java/org/apache/ratis/netty/MiniRaftClusterWithNetty.java
@@ -40,6 +40,13 @@ public class MiniRaftClusterWithNetty extends MiniRaftCluster.RpcBase {
     }
   };
 
+  public interface FactoryGet extends Factory.Get<MiniRaftClusterWithNetty> {
+    @Override
+    default Factory<MiniRaftClusterWithNetty> getFactory() {
+      return FACTORY;
+    }
+  }
+
   public static final DelayLocalExecutionInjection sendServerRequest
       = new DelayLocalExecutionInjection(NettyRpcService.SEND_SERVER_REQUEST);
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-netty/src/test/java/org/apache/ratis/netty/TestNotLeaderExceptionWithNetty.java
----------------------------------------------------------------------
diff --git a/ratis-netty/src/test/java/org/apache/ratis/netty/TestNotLeaderExceptionWithNetty.java b/ratis-netty/src/test/java/org/apache/ratis/netty/TestNotLeaderExceptionWithNetty.java
deleted file mode 100644
index 6e4ed12..0000000
--- a/ratis-netty/src/test/java/org/apache/ratis/netty/TestNotLeaderExceptionWithNetty.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.ratis.netty;
-
-import org.apache.ratis.MiniRaftCluster;
-import org.apache.ratis.RaftNotLeaderExceptionBaseTest;
-import org.apache.ratis.conf.RaftProperties;
-
-import java.io.IOException;
-
-public class TestNotLeaderExceptionWithNetty extends RaftNotLeaderExceptionBaseTest {
-  @Override
-  public MiniRaftCluster.Factory<?> getFactory() {
-    return MiniRaftClusterWithNetty.FACTORY;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-netty/src/test/java/org/apache/ratis/netty/TestRaftExceptionWithNetty.java
----------------------------------------------------------------------
diff --git a/ratis-netty/src/test/java/org/apache/ratis/netty/TestRaftExceptionWithNetty.java b/ratis-netty/src/test/java/org/apache/ratis/netty/TestRaftExceptionWithNetty.java
new file mode 100644
index 0000000..3414a65
--- /dev/null
+++ b/ratis-netty/src/test/java/org/apache/ratis/netty/TestRaftExceptionWithNetty.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.netty;
+
+import org.apache.ratis.RaftExceptionBaseTest;
+
+public class TestRaftExceptionWithNetty
+    extends RaftExceptionBaseTest<MiniRaftClusterWithNetty>
+    implements MiniRaftClusterWithNetty.FactoryGet {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
index 4463914..2d38fe7 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
@@ -351,25 +351,19 @@ public class RaftServerImpl implements RaftServerProtocol,
    */
   private CompletableFuture<RaftClientReply> checkLeaderState(
       RaftClientRequest request, RetryCache.CacheEntry entry) {
+    try {
+      assertGroup(request.getRequestorId(), request.getRaftGroupId());
+    } catch (GroupMismatchException e) {
+      return RetryCache.failWithException(e, entry);
+    }
+
     if (!isLeader()) {
       NotLeaderException exception = generateNotLeaderException();
       final RaftClientReply reply = new RaftClientReply(request, exception);
-      if (entry != null) {
-        entry.failWithReply(reply);
-      }
-      return entry != null ?
-          entry.getReplyFuture() : CompletableFuture.completedFuture(reply);
+      return RetryCache.failWithReply(reply, entry);
     } else {
       if (leaderState == null || !leaderState.isReady()) {
-        final Exception e = new LeaderNotReadyException();
-        if (entry != null) {
-          entry.failWithException(e);
-          return entry.getReplyFuture();
-        } else {
-          CompletableFuture<RaftClientReply> future = new CompletableFuture<>();
-          future.completeExceptionally(e);
-          return future;
-        }
+        return RetryCache.failWithException(new LeaderNotReadyException(), entry);
       }
     }
     return null;
@@ -476,7 +470,7 @@ public class RaftServerImpl implements RaftServerProtocol,
     TransactionContext context = stateMachine.startTransaction(request);
     if (context.getException() != null) {
       RaftClientReply exceptionReply = new RaftClientReply(request,
-          new StateMachineException(getId().toString(), context.getException()));
+          new StateMachineException(getId(), context.getException()));
       cacheEntry.failWithReply(exceptionReply);
       return CompletableFuture.completedFuture(exceptionReply);
     }
@@ -526,6 +520,8 @@ public class RaftServerImpl implements RaftServerProtocol,
       SetConfigurationRequest request) throws IOException {
     LOG.debug("{}: receive setConfiguration({})", getId(), request);
     assertLifeCycleState(RUNNING);
+    assertGroup(request.getRequestorId(), request.getRaftGroupId());
+
     CompletableFuture<RaftClientReply> reply = checkLeaderState(request, null);
     if (reply != null) {
       return reply;
@@ -914,8 +910,7 @@ public class RaftServerImpl implements RaftServerProtocol,
       } else {
         // the exception is coming from the state machine. wrap it into the
         // reply as a StateMachineException
-        final StateMachineException e = new StateMachineException(
-            getId().toString(), exception);
+        final StateMachineException e = new StateMachineException(getId(), exception);
         r = new RaftClientReply(clientId, serverId, groupId, callId, false, null, e);
       }
       // update retry cache

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerProxy.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerProxy.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerProxy.java
index 9be1e9a..554372c 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerProxy.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerProxy.java
@@ -158,9 +158,11 @@ public class RaftServerProxy implements RaftServer {
   @Override
   public CompletableFuture<RaftClientReply> reinitializeAsync(
       ReinitializeRequest request) throws IOException {
+    getImpl().assertGroup(request.getRequestorId(), request.getRaftGroupId());
     if (!reinitializeRequest.compareAndSet(null, request)) {
       throw new IOException("Another reinitialize is already in progress.");
     }
+
     return CompletableFuture.supplyAsync(() -> {
       try {
         final CompletableFuture<RaftServerImpl> oldImpl = impl;

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-server/src/main/java/org/apache/ratis/server/impl/RetryCache.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/RetryCache.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/RetryCache.java
index bf2e94c..4e65124 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/RetryCache.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/RetryCache.java
@@ -80,7 +80,7 @@ public class RetryCache implements Closeable {
     /**
      * "failed" means we failed to commit the request into the raft group, or
      * the request did not get approved by the state machine before the raft
-     * replication. Not once the request gets committed by the raft group, this
+     * replication. Note once the request gets committed by the raft group, this
      * field is never true even if the state machine throws an exception when
      * applying the transaction.
      */
@@ -222,4 +222,27 @@ public class RetryCache implements Closeable {
       cache.invalidateAll();
     }
   }
+
+  static CompletableFuture<RaftClientReply> failWithReply(
+      RaftClientReply reply, CacheEntry entry) {
+    if (entry != null) {
+      entry.failWithReply(reply);
+      return entry.getReplyFuture();
+    } else {
+      return CompletableFuture.completedFuture(reply);
+    }
+  }
+
+  static CompletableFuture<RaftClientReply> failWithException(
+      Throwable t, CacheEntry entry) {
+    if (entry != null) {
+      entry.failWithException(t);
+      return entry.getReplyFuture();
+    } else {
+      final CompletableFuture<RaftClientReply> future = new CompletableFuture<>();
+      future.completeExceptionally(t);
+      return future;
+    }
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-server/src/main/java/org/apache/ratis/server/storage/RaftLog.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/storage/RaftLog.java b/ratis-server/src/main/java/org/apache/ratis/server/storage/RaftLog.java
index ee765c1..ac86582 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/storage/RaftLog.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/storage/RaftLog.java
@@ -136,7 +136,7 @@ public abstract class RaftLog implements Closeable {
       try {
         operation = operation.preAppendTransaction();
       } catch (IOException e) {
-        throw new StateMachineException(selfId.toString(), e);
+        throw new StateMachineException(selfId, e);
       }
 
       // build the log entry after calling the StateMachine

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java b/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java
index 79cb9bb..a50c0d7 100644
--- a/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java
+++ b/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java
@@ -56,6 +56,10 @@ public abstract class MiniRaftCluster {
   public static final Class<? extends StateMachine> STATEMACHINE_CLASS_DEFAULT = BaseStateMachine.class;
 
   public static abstract class Factory<CLUSTER extends MiniRaftCluster> {
+    public interface Get<CLUSTER extends MiniRaftCluster> {
+      Factory<CLUSTER> getFactory();
+    }
+
     public abstract CLUSTER newCluster(
         String[] ids, RaftProperties prop);
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-server/src/test/java/org/apache/ratis/RaftExceptionBaseTest.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/RaftExceptionBaseTest.java b/ratis-server/src/test/java/org/apache/ratis/RaftExceptionBaseTest.java
new file mode 100644
index 0000000..eceaf20
--- /dev/null
+++ b/ratis-server/src/test/java/org/apache/ratis/RaftExceptionBaseTest.java
@@ -0,0 +1,199 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis;
+
+import org.apache.log4j.Level;
+import org.apache.ratis.RaftTestUtil.SimpleMessage;
+import org.apache.ratis.client.RaftClient;
+import org.apache.ratis.client.RaftClientRpc;
+import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.protocol.*;
+import org.apache.ratis.server.impl.RaftServerImpl;
+import org.apache.ratis.server.storage.RaftLog;
+import org.apache.ratis.shaded.com.google.protobuf.ByteString;
+import org.apache.ratis.util.LogUtils;
+import org.junit.*;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.apache.ratis.server.impl.RaftServerConstants.DEFAULT_CALLID;
+
+public abstract class RaftExceptionBaseTest<CLUSTER extends MiniRaftCluster>
+    extends BaseTest
+    implements MiniRaftCluster.Factory.Get<CLUSTER> {
+  static {
+    LogUtils.setLogLevel(RaftServerImpl.LOG, Level.DEBUG);
+    LogUtils.setLogLevel(RaftLog.LOG, Level.DEBUG);
+    LogUtils.setLogLevel(RaftClient.LOG, Level.DEBUG);
+  }
+
+  public static final int NUM_PEERS = 5;
+
+  private CLUSTER cluster;
+
+  @Before
+  public void setup() throws IOException {
+    cluster = getFactory().newCluster(NUM_PEERS, new RaftProperties());
+    cluster.start();
+  }
+
+  @After
+  public void tearDown() {
+    if (cluster != null) {
+      cluster.shutdown();
+    }
+  }
+
+  @Test
+  public void testHandleNotLeaderException() throws Exception {
+    testHandleNotLeaderException(false);
+  }
+
+  /**
+   * Test handle both IOException and NotLeaderException
+   */
+  @Test
+  public void testHandleNotLeaderAndIOException() throws Exception {
+    testHandleNotLeaderException(true);
+  }
+
+  private void testHandleNotLeaderException(boolean killNewLeader)
+      throws Exception {
+    RaftTestUtil.waitForLeader(cluster);
+    final RaftPeerId leaderId = cluster.getLeader().getId();
+    final RaftClient client = cluster.createClient(leaderId);
+
+    RaftClientReply reply = client.send(new SimpleMessage("m1"));
+    Assert.assertTrue(reply.isSuccess());
+
+    // enforce leader change
+    RaftPeerId newLeader = RaftTestUtil.changeLeader(cluster, leaderId);
+    Assert.assertNotEquals(leaderId, newLeader);
+
+    if (killNewLeader) {
+      // kill the new leader
+      cluster.killServer(newLeader);
+    }
+
+    RaftClientRpc rpc = client.getClientRpc();
+    reply= null;
+    for (int i = 0; reply == null && i < 10; i++) {
+      try {
+        reply = rpc.sendRequest(
+            new RaftClientRequest(ClientId.randomId(), leaderId,
+                cluster.getGroupId(), DEFAULT_CALLID,
+                new SimpleMessage("m2")));
+      } catch (IOException ignored) {
+        Thread.sleep(1000);
+      }
+    }
+    Assert.assertNotNull(reply);
+    Assert.assertFalse(reply.isSuccess());
+    Assert.assertTrue(reply.isNotLeader());
+    Assert.assertEquals(newLeader,
+        reply.getNotLeaderException().getSuggestedLeader().getId());
+
+    reply = client.send(new SimpleMessage("m3"));
+    Assert.assertTrue(reply.isSuccess());
+    client.close();
+  }
+
+  @Test
+  public void testNotLeaderExceptionWithReconf() throws Exception {
+    Assert.assertNotNull(RaftTestUtil.waitForLeader(cluster));
+
+    final RaftPeerId leaderId = cluster.getLeader().getId();
+    final RaftClient client = cluster.createClient(leaderId);
+
+    // enforce leader change
+    RaftPeerId newLeader = RaftTestUtil.changeLeader(cluster, leaderId);
+    Assert.assertNotEquals(leaderId, newLeader);
+
+    // also add two new peers
+    // add two more peers
+    MiniRaftCluster.PeerChanges change = cluster.addNewPeers(
+        new String[]{"ss1", "ss2"}, true);
+    // trigger setConfiguration
+    LOG.info("Start changing the configuration: {}",
+        Arrays.asList(change.allPeersInNewConf));
+    try(final RaftClient c2 = cluster.createClient(newLeader)) {
+      RaftClientReply reply = c2.setConfiguration(change.allPeersInNewConf);
+      Assert.assertTrue(reply.isSuccess());
+    }
+    LOG.info(cluster.printServers());
+
+    RaftClientRpc rpc = client.getClientRpc();
+    RaftClientReply reply = null;
+    // it is possible that the remote peer's rpc server is not ready. need retry
+    for (int i = 0; reply == null && i < 10; i++) {
+      try {
+        reply = rpc.sendRequest(
+            new RaftClientRequest(ClientId.randomId(), leaderId,
+                cluster.getGroupId(), DEFAULT_CALLID,
+                new SimpleMessage("m1")));
+      } catch (IOException ignored) {
+        Thread.sleep(1000);
+      }
+    }
+    Assert.assertNotNull(reply);
+    Assert.assertFalse(reply.isSuccess());
+    Assert.assertTrue(reply.isNotLeader());
+    Assert.assertEquals(newLeader,
+        reply.getNotLeaderException().getSuggestedLeader().getId());
+    Collection<RaftPeer> peers = cluster.getPeers();
+    RaftPeer[] peersFromReply = reply.getNotLeaderException().getPeers();
+    Assert.assertEquals(peers.size(), peersFromReply.length);
+    for (RaftPeer p : peersFromReply) {
+      Assert.assertTrue(peers.contains(p));
+    }
+
+    reply = client.send(new SimpleMessage("m2"));
+    Assert.assertTrue(reply.isSuccess());
+    client.close();
+  }
+
+  @Test
+  public void testGroupMismatchException() throws Exception {
+    final RaftGroup clusterGroup = cluster.getGroup();
+    Assert.assertEquals(NUM_PEERS, clusterGroup.getPeers().size());
+
+    final RaftGroup anotherGroup = new RaftGroup(RaftGroupId.randomId(), clusterGroup.getPeers());
+    Assert.assertNotEquals(clusterGroup.getGroupId(), anotherGroup.getGroupId());
+
+    // Create client using another group
+    try(RaftClient client = cluster.createClient(anotherGroup)) {
+      testFailureCase("send(..) with client group being different from the server group",
+          () -> client.send(() -> ByteString.EMPTY),
+          GroupMismatchException.class);
+
+      testFailureCase("sendReadOnly(..) with client group being different from the server group",
+          () -> client.sendReadOnly(() -> ByteString.EMPTY),
+          GroupMismatchException.class);
+
+      testFailureCase("setConfiguration(..) with client group being different from the server group",
+          () -> client.setConfiguration(RaftPeer.emptyArray()),
+          GroupMismatchException.class);
+
+      testFailureCase("reinitialize(..) with client group being different from the server group",
+          () -> client.reinitialize(anotherGroup, clusterGroup.getPeers().get(0).getId()),
+          GroupMismatchException.class);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-server/src/test/java/org/apache/ratis/RaftNotLeaderExceptionBaseTest.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/RaftNotLeaderExceptionBaseTest.java b/ratis-server/src/test/java/org/apache/ratis/RaftNotLeaderExceptionBaseTest.java
deleted file mode 100644
index 583aac7..0000000
--- a/ratis-server/src/test/java/org/apache/ratis/RaftNotLeaderExceptionBaseTest.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.ratis;
-
-import org.apache.log4j.Level;
-import org.apache.ratis.RaftTestUtil.SimpleMessage;
-import org.apache.ratis.client.RaftClient;
-import org.apache.ratis.client.RaftClientRpc;
-import org.apache.ratis.conf.RaftProperties;
-import org.apache.ratis.protocol.*;
-import org.apache.ratis.server.impl.RaftServerImpl;
-import org.apache.ratis.server.storage.RaftLog;
-import org.apache.ratis.util.LogUtils;
-import org.junit.*;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-
-import static org.apache.ratis.server.impl.RaftServerConstants.DEFAULT_CALLID;
-
-public abstract class RaftNotLeaderExceptionBaseTest extends BaseTest {
-  static {
-    LogUtils.setLogLevel(RaftServerImpl.LOG, Level.DEBUG);
-    LogUtils.setLogLevel(RaftLog.LOG, Level.DEBUG);
-    LogUtils.setLogLevel(RaftClient.LOG, Level.DEBUG);
-  }
-
-  public static final int NUM_PEERS = 5;
-
-  private MiniRaftCluster cluster;
-
-  public abstract MiniRaftCluster.Factory<?> getFactory();
-
-  @Before
-  public void setup() throws IOException {
-    cluster = getFactory().newCluster(NUM_PEERS, new RaftProperties());
-    cluster.start();
-  }
-
-  @After
-  public void tearDown() {
-    if (cluster != null) {
-      cluster.shutdown();
-    }
-  }
-
-  @Test
-  public void testHandleNotLeaderException() throws Exception {
-    testHandleNotLeaderException(false);
-  }
-
-  /**
-   * Test handle both IOException and NotLeaderException
-   */
-  @Test
-  public void testHandleNotLeaderAndIOException() throws Exception {
-    testHandleNotLeaderException(true);
-  }
-
-  private void testHandleNotLeaderException(boolean killNewLeader)
-      throws Exception {
-    RaftTestUtil.waitForLeader(cluster);
-    final RaftPeerId leaderId = cluster.getLeader().getId();
-    final RaftClient client = cluster.createClient(leaderId);
-
-    RaftClientReply reply = client.send(new SimpleMessage("m1"));
-    Assert.assertTrue(reply.isSuccess());
-
-    // enforce leader change
-    RaftPeerId newLeader = RaftTestUtil.changeLeader(cluster, leaderId);
-    Assert.assertNotEquals(leaderId, newLeader);
-
-    if (killNewLeader) {
-      // kill the new leader
-      cluster.killServer(newLeader);
-    }
-
-    RaftClientRpc rpc = client.getClientRpc();
-    reply= null;
-    for (int i = 0; reply == null && i < 10; i++) {
-      try {
-        reply = rpc.sendRequest(
-            new RaftClientRequest(ClientId.randomId(), leaderId,
-                cluster.getGroupId(), DEFAULT_CALLID,
-                new SimpleMessage("m2")));
-      } catch (IOException ignored) {
-        Thread.sleep(1000);
-      }
-    }
-    Assert.assertNotNull(reply);
-    Assert.assertFalse(reply.isSuccess());
-    Assert.assertTrue(reply.isNotLeader());
-    Assert.assertEquals(newLeader,
-        reply.getNotLeaderException().getSuggestedLeader().getId());
-
-    reply = client.send(new SimpleMessage("m3"));
-    Assert.assertTrue(reply.isSuccess());
-    client.close();
-  }
-
-  @Test
-  public void testNotLeaderExceptionWithReconf() throws Exception {
-    Assert.assertNotNull(RaftTestUtil.waitForLeader(cluster));
-
-    final RaftPeerId leaderId = cluster.getLeader().getId();
-    final RaftClient client = cluster.createClient(leaderId);
-
-    // enforce leader change
-    RaftPeerId newLeader = RaftTestUtil.changeLeader(cluster, leaderId);
-    Assert.assertNotEquals(leaderId, newLeader);
-
-    // also add two new peers
-    // add two more peers
-    MiniRaftCluster.PeerChanges change = cluster.addNewPeers(
-        new String[]{"ss1", "ss2"}, true);
-    // trigger setConfiguration
-    LOG.info("Start changing the configuration: {}",
-        Arrays.asList(change.allPeersInNewConf));
-    try(final RaftClient c2 = cluster.createClient(newLeader)) {
-      RaftClientReply reply = c2.setConfiguration(change.allPeersInNewConf);
-      Assert.assertTrue(reply.isSuccess());
-    }
-    LOG.info(cluster.printServers());
-
-    RaftClientRpc rpc = client.getClientRpc();
-    RaftClientReply reply = null;
-    // it is possible that the remote peer's rpc server is not ready. need retry
-    for (int i = 0; reply == null && i < 10; i++) {
-      try {
-        reply = rpc.sendRequest(
-            new RaftClientRequest(ClientId.randomId(), leaderId,
-                cluster.getGroupId(), DEFAULT_CALLID,
-                new SimpleMessage("m1")));
-      } catch (IOException ignored) {
-        Thread.sleep(1000);
-      }
-    }
-    Assert.assertNotNull(reply);
-    Assert.assertFalse(reply.isSuccess());
-    Assert.assertTrue(reply.isNotLeader());
-    Assert.assertEquals(newLeader,
-        reply.getNotLeaderException().getSuggestedLeader().getId());
-    Collection<RaftPeer> peers = cluster.getPeers();
-    RaftPeer[] peersFromReply = reply.getNotLeaderException().getPeers();
-    Assert.assertEquals(peers.size(), peersFromReply.length);
-    for (RaftPeer p : peersFromReply) {
-      Assert.assertTrue(peers.contains(p));
-    }
-
-    reply = client.send(new SimpleMessage("m2"));
-    Assert.assertTrue(reply.isSuccess());
-    client.close();
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-server/src/test/java/org/apache/ratis/server/impl/ReinitializationBaseTest.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/server/impl/ReinitializationBaseTest.java b/ratis-server/src/test/java/org/apache/ratis/server/impl/ReinitializationBaseTest.java
index d9068d1..251a4d3 100644
--- a/ratis-server/src/test/java/org/apache/ratis/server/impl/ReinitializationBaseTest.java
+++ b/ratis-server/src/test/java/org/apache/ratis/server/impl/ReinitializationBaseTest.java
@@ -149,7 +149,7 @@ public abstract class ReinitializationBaseTest extends BaseTest {
     for (int i = 0; i < idIndex.length; i++) {
       final RaftGroupId gid = RaftGroupId.randomId();
       final int previous = i == 0 ? 0 : idIndex[i - 1];
-      final RaftPeer[] peers = allPeers.subList(previous, idIndex[i]).toArray(RaftPeer.EMPTY_PEERS);
+      final RaftPeer[] peers = allPeers.subList(previous, idIndex[i]).toArray(RaftPeer.emptyArray());
       groups[i] = new RaftGroup(gid, peers);
 
       LOG.info(i + ") starting " + groups[i]);
@@ -196,7 +196,7 @@ public abstract class ReinitializationBaseTest extends BaseTest {
     }
     LOG.info(chosen + ") setConfiguration: " + cluster.printServers(groups[chosen].getGroupId()));
     try (final RaftClient client = cluster.createClient(groups[chosen])) {
-      client.setConfiguration(allPeers.toArray(RaftPeer.EMPTY_PEERS));
+      client.setConfiguration(allPeers.toArray(RaftPeer.emptyArray()));
     }
 
     Assert.assertNotNull(RaftTestUtil.waitForLeader(cluster, true));

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-server/src/test/java/org/apache/ratis/server/simulation/MiniRaftClusterWithSimulatedRpc.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/server/simulation/MiniRaftClusterWithSimulatedRpc.java b/ratis-server/src/test/java/org/apache/ratis/server/simulation/MiniRaftClusterWithSimulatedRpc.java
index b3b679f..c2851d1 100644
--- a/ratis-server/src/test/java/org/apache/ratis/server/simulation/MiniRaftClusterWithSimulatedRpc.java
+++ b/ratis-server/src/test/java/org/apache/ratis/server/simulation/MiniRaftClusterWithSimulatedRpc.java
@@ -59,6 +59,13 @@ public class MiniRaftClusterWithSimulatedRpc extends MiniRaftCluster {
     }
   };
 
+  public interface FactoryGet extends Factory.Get<MiniRaftClusterWithSimulatedRpc> {
+    @Override
+    default Factory<MiniRaftClusterWithSimulatedRpc> getFactory() {
+      return FACTORY;
+    }
+  }
+
   private final SimulatedRequestReply<RaftServerRequest, RaftServerReply> serverRequestReply;
   private final SimulatedClientRpc client2serverRequestReply;
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestNotLeaderExceptionWithSimulation.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestNotLeaderExceptionWithSimulation.java b/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestNotLeaderExceptionWithSimulation.java
deleted file mode 100644
index c37e2fb..0000000
--- a/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestNotLeaderExceptionWithSimulation.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.ratis.server.simulation;
-
-import org.apache.ratis.MiniRaftCluster;
-import org.apache.ratis.RaftNotLeaderExceptionBaseTest;
-
-public class TestNotLeaderExceptionWithSimulation extends RaftNotLeaderExceptionBaseTest {
-  @Override
-  public MiniRaftCluster.Factory<?> getFactory() {
-    return MiniRaftClusterWithSimulatedRpc.FACTORY;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/9ce1783d/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestRaftExceptionWithSimulation.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestRaftExceptionWithSimulation.java b/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestRaftExceptionWithSimulation.java
new file mode 100644
index 0000000..f328ea3
--- /dev/null
+++ b/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestRaftExceptionWithSimulation.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.ratis.server.simulation;
+
+import org.apache.ratis.RaftExceptionBaseTest;
+
+public class TestRaftExceptionWithSimulation
+    extends RaftExceptionBaseTest<MiniRaftClusterWithSimulatedRpc>
+    implements MiniRaftClusterWithSimulatedRpc.FactoryGet {
+}