Repository: incubator-ratis
Updated Branches:
  refs/heads/master d960d1e7f -> 813db4b78


http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/main/java/org/apache/raft/statemachine/SnapshotInfo.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/main/java/org/apache/raft/statemachine/SnapshotInfo.java 
b/raft-server/src/main/java/org/apache/raft/statemachine/SnapshotInfo.java
index 6d82942..b7deb9a 100644
--- a/raft-server/src/main/java/org/apache/raft/statemachine/SnapshotInfo.java
+++ b/raft-server/src/main/java/org/apache/raft/statemachine/SnapshotInfo.java
@@ -17,7 +17,7 @@
  */
 package org.apache.raft.statemachine;
 
-import org.apache.raft.server.RaftConfiguration;
+import org.apache.raft.server.impl.RaftConfiguration;
 import org.apache.raft.server.protocol.TermIndex;
 import org.apache.raft.server.storage.FileInfo;
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/main/java/org/apache/raft/statemachine/SnapshotInfoImpl.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/main/java/org/apache/raft/statemachine/SnapshotInfoImpl.java 
b/raft-server/src/main/java/org/apache/raft/statemachine/SnapshotInfoImpl.java
index af8ba0e..670bfc7 100644
--- 
a/raft-server/src/main/java/org/apache/raft/statemachine/SnapshotInfoImpl.java
+++ 
b/raft-server/src/main/java/org/apache/raft/statemachine/SnapshotInfoImpl.java
@@ -17,7 +17,7 @@
  */
 package org.apache.raft.statemachine;
 
-import org.apache.raft.server.RaftConfiguration;
+import org.apache.raft.server.impl.RaftConfiguration;
 import org.apache.raft.server.protocol.TermIndex;
 import org.apache.raft.server.storage.FileInfo;
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/main/java/org/apache/raft/statemachine/StateMachine.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/main/java/org/apache/raft/statemachine/StateMachine.java 
b/raft-server/src/main/java/org/apache/raft/statemachine/StateMachine.java
index dc18175..935a83a 100644
--- a/raft-server/src/main/java/org/apache/raft/statemachine/StateMachine.java
+++ b/raft-server/src/main/java/org/apache/raft/statemachine/StateMachine.java
@@ -17,20 +17,20 @@
  */
 package org.apache.raft.statemachine;
 
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.concurrent.CompletableFuture;
-
 import org.apache.raft.conf.RaftProperties;
 import org.apache.raft.protocol.Message;
 import org.apache.raft.protocol.RaftClientReply;
 import org.apache.raft.protocol.RaftClientRequest;
-import org.apache.raft.server.RaftConfiguration;
 import org.apache.raft.server.RaftServerConfigKeys;
+import org.apache.raft.server.impl.RaftConfiguration;
 import org.apache.raft.server.storage.RaftStorage;
 import org.apache.raft.util.LifeCycle;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.concurrent.CompletableFuture;
+
 public interface StateMachine extends Closeable {
   /**
    * Initializes the State Machine with the given properties and storage. The 
state machine is

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/main/java/org/apache/raft/statemachine/TermIndexTracker.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/main/java/org/apache/raft/statemachine/TermIndexTracker.java 
b/raft-server/src/main/java/org/apache/raft/statemachine/TermIndexTracker.java
index 1548e1d..694eef4 100644
--- 
a/raft-server/src/main/java/org/apache/raft/statemachine/TermIndexTracker.java
+++ 
b/raft-server/src/main/java/org/apache/raft/statemachine/TermIndexTracker.java
@@ -20,7 +20,7 @@ package org.apache.raft.statemachine;
 import com.google.common.base.Preconditions;
 import org.apache.raft.server.protocol.TermIndex;
 
-import static org.apache.raft.server.RaftServerConstants.INVALID_LOG_INDEX;
+import static 
org.apache.raft.server.impl.RaftServerConstants.INVALID_LOG_INDEX;
 
 /**
  * Tracks the term index that is applied to the StateMachine for simple state 
machines with

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/MiniRaftCluster.java
----------------------------------------------------------------------
diff --git a/raft-server/src/test/java/org/apache/raft/MiniRaftCluster.java 
b/raft-server/src/test/java/org/apache/raft/MiniRaftCluster.java
index 80cfa1f..4f0871f 100644
--- a/raft-server/src/test/java/org/apache/raft/MiniRaftCluster.java
+++ b/raft-server/src/test/java/org/apache/raft/MiniRaftCluster.java
@@ -23,7 +23,11 @@ import org.apache.raft.client.RaftClientRequestSender;
 import org.apache.raft.client.impl.RaftClientImpl;
 import org.apache.raft.conf.RaftProperties;
 import org.apache.raft.protocol.RaftPeer;
-import org.apache.raft.server.*;
+import org.apache.raft.server.RaftServerConfigKeys;
+import org.apache.raft.server.impl.DelayLocalExecutionInjection;
+import org.apache.raft.server.impl.RaftConfiguration;
+import org.apache.raft.server.impl.RaftServer;
+import org.apache.raft.server.impl.RaftServerRpc;
 import org.apache.raft.server.storage.MemoryRaftLog;
 import org.apache.raft.server.storage.RaftLog;
 import org.apache.raft.statemachine.BaseStateMachine;
@@ -40,7 +44,7 @@ import java.io.IOException;
 import java.util.*;
 import java.util.stream.Collectors;
 
-import static org.apache.raft.server.RaftServerConfigKeys.*;
+import static 
org.apache.raft.server.RaftServerConfigKeys.RAFT_SERVER_RPC_TIMEOUT_MIN_MS_DEFAULT;
 
 public abstract class MiniRaftCluster {
   public static final Logger LOG = 
LoggerFactory.getLogger(MiniRaftCluster.class);

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/RaftBasicTests.java
----------------------------------------------------------------------
diff --git a/raft-server/src/test/java/org/apache/raft/RaftBasicTests.java 
b/raft-server/src/test/java/org/apache/raft/RaftBasicTests.java
index 5a35afe..921e063 100644
--- a/raft-server/src/test/java/org/apache/raft/RaftBasicTests.java
+++ b/raft-server/src/test/java/org/apache/raft/RaftBasicTests.java
@@ -20,12 +20,8 @@ package org.apache.raft;
 import org.apache.raft.RaftTestUtil.SimpleMessage;
 import org.apache.raft.client.RaftClient;
 import org.apache.raft.conf.RaftProperties;
-import org.apache.raft.server.RaftServer;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
+import org.apache.raft.server.impl.RaftServer;
+import org.junit.*;
 import org.junit.rules.Timeout;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/RaftNotLeaderExceptionBaseTest.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/RaftNotLeaderExceptionBaseTest.java 
b/raft-server/src/test/java/org/apache/raft/RaftNotLeaderExceptionBaseTest.java
index 46a30c7..8a249e9 100644
--- 
a/raft-server/src/test/java/org/apache/raft/RaftNotLeaderExceptionBaseTest.java
+++ 
b/raft-server/src/test/java/org/apache/raft/RaftNotLeaderExceptionBaseTest.java
@@ -25,15 +25,11 @@ import org.apache.raft.client.impl.RaftClientImpl;
 import org.apache.raft.protocol.RaftClientReply;
 import org.apache.raft.protocol.RaftClientRequest;
 import org.apache.raft.protocol.RaftPeer;
-import org.apache.raft.server.RaftServer;
+import org.apache.raft.server.impl.RaftServer;
 import org.apache.raft.server.simulation.RequestHandler;
 import org.apache.raft.server.storage.RaftLog;
 import org.apache.raft.util.RaftUtils;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.*;
 import org.junit.rules.Timeout;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -42,7 +38,7 @@ import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
 
-import static org.apache.raft.server.RaftServerConstants.DEFAULT_SEQNUM;
+import static org.apache.raft.server.impl.RaftServerConstants.DEFAULT_SEQNUM;
 
 public abstract class RaftNotLeaderExceptionBaseTest {
   static {

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/RaftTestUtil.java
----------------------------------------------------------------------
diff --git a/raft-server/src/test/java/org/apache/raft/RaftTestUtil.java 
b/raft-server/src/test/java/org/apache/raft/RaftTestUtil.java
index ff7921d..92bf5c4 100644
--- a/raft-server/src/test/java/org/apache/raft/RaftTestUtil.java
+++ b/raft-server/src/test/java/org/apache/raft/RaftTestUtil.java
@@ -20,10 +20,10 @@ package org.apache.raft;
 import com.google.common.base.Preconditions;
 import org.apache.commons.lang.RandomStringUtils;
 import org.apache.raft.protocol.Message;
-import org.apache.raft.server.BlockRequestHandlingInjection;
-import org.apache.raft.server.DelayLocalExecutionInjection;
-import org.apache.raft.server.RaftServer;
 import org.apache.raft.server.RaftServerConfigKeys;
+import org.apache.raft.server.impl.BlockRequestHandlingInjection;
+import org.apache.raft.server.impl.DelayLocalExecutionInjection;
+import org.apache.raft.server.impl.RaftServer;
 import org.apache.raft.shaded.com.google.protobuf.ByteString;
 import org.apache.raft.shaded.proto.RaftProtos.LogEntryProto;
 import org.apache.raft.shaded.proto.RaftProtos.SMLogEntryProto;

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/BlockRequestHandlingInjection.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/BlockRequestHandlingInjection.java
 
b/raft-server/src/test/java/org/apache/raft/server/BlockRequestHandlingInjection.java
deleted file mode 100644
index e1de3e5..0000000
--- 
a/raft-server/src/test/java/org/apache/raft/server/BlockRequestHandlingInjection.java
+++ /dev/null
@@ -1,84 +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.raft.server;
-
-import org.apache.raft.RaftTestUtil;
-import org.apache.raft.util.CodeInjectionForTesting;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/** Inject code to block a server from handling incoming requests. */
-public class BlockRequestHandlingInjection implements 
CodeInjectionForTesting.Code {
-  private static final BlockRequestHandlingInjection INSTANCE =
-      new BlockRequestHandlingInjection();
-
-  static {
-    CodeInjectionForTesting.put(RaftServer.REQUEST_VOTE, INSTANCE);
-    CodeInjectionForTesting.put(RaftServer.APPEND_ENTRIES, INSTANCE);
-    CodeInjectionForTesting.put(RaftServer.INSTALL_SNAPSHOT, INSTANCE);
-  }
-
-  public static BlockRequestHandlingInjection getInstance() {
-    return INSTANCE;
-  }
-
-  private final Map<String, Boolean> requestors = new ConcurrentHashMap<>();
-  private final Map<String, Boolean> repliers = new ConcurrentHashMap<>();
-
-  private BlockRequestHandlingInjection() {}
-
-  public void blockRequestor(String requestor) {
-    requestors.put(requestor, true);
-  }
-
-  public void unblockRequestor(String requestor) {
-    requestors.remove(requestor);
-  }
-
-  public void blockReplier(String replier) {
-    repliers.put(replier, true);
-  }
-
-  public void unblockReplier(String replier) {
-    repliers.remove(replier);
-  }
-
-  public void unblockAll() {
-    requestors.clear();
-    repliers.clear();
-  }
-
-  @Override
-  public boolean execute(String localId, String remoteId, Object... args) {
-    if (shouldBlock(localId, remoteId)) {
-      try {
-        RaftTestUtil.block(() -> shouldBlock(localId, remoteId));
-        return true;
-      } catch (InterruptedException e) {
-        LOG.debug("Interrupted while blocking request handling from " + 
remoteId
-            + " to " + localId);
-      }
-    }
-    return false;
-  }
-
-  private boolean shouldBlock(String localId, String remoteId) {
-    return repliers.containsKey(localId) || requestors.containsKey(remoteId);
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/DelayLocalExecutionInjection.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/DelayLocalExecutionInjection.java
 
b/raft-server/src/test/java/org/apache/raft/server/DelayLocalExecutionInjection.java
deleted file mode 100644
index 612b75f..0000000
--- 
a/raft-server/src/test/java/org/apache/raft/server/DelayLocalExecutionInjection.java
+++ /dev/null
@@ -1,67 +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.raft.server;
-
-import org.apache.raft.RaftTestUtil;
-import org.apache.raft.util.CodeInjectionForTesting;
-
-import java.util.Arrays;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/** Inject code to delay particular servers. */
-public class DelayLocalExecutionInjection implements 
CodeInjectionForTesting.Code {
-  private final Map<String, AtomicInteger> delays = new ConcurrentHashMap<>();
-
-  public DelayLocalExecutionInjection(String method) {
-    CodeInjectionForTesting.put(method, this);
-  }
-
-  public void clear() {
-    delays.clear();
-  }
-
-  public void setDelayMs(String id, int delayMs) {
-    AtomicInteger d = delays.get(id);
-    if (d == null) {
-      delays.put(id, d = new AtomicInteger());
-    }
-    d.set(delayMs);
-  }
-
-  public void removeDelay(String id) {
-    delays.remove(id);
-  }
-
-  @Override
-  public boolean execute(String localId, String remoteId, Object... args) {
-    final AtomicInteger d = delays.get(localId);
-    if (d == null) {
-      return false;
-    }
-    LOG.info("{} delay {} ms, args={}", localId, d.get(),
-        Arrays.toString(args));
-    try {
-      RaftTestUtil.delay(d::get);
-    } catch (InterruptedException e) {
-      LOG.debug("Interrupted while delaying " + localId);
-    }
-    return true;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/RaftReconfigurationBaseTest.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/RaftReconfigurationBaseTest.java
 
b/raft-server/src/test/java/org/apache/raft/server/RaftReconfigurationBaseTest.java
deleted file mode 100644
index 1be0cd7..0000000
--- 
a/raft-server/src/test/java/org/apache/raft/server/RaftReconfigurationBaseTest.java
+++ /dev/null
@@ -1,576 +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.raft.server;
-
-import org.apache.log4j.Level;
-import org.apache.raft.MiniRaftCluster;
-import org.apache.raft.MiniRaftCluster.PeerChanges;
-import org.apache.raft.RaftTestUtil;
-import org.apache.raft.RaftTestUtil.SimpleMessage;
-import org.apache.raft.client.RaftClient;
-import org.apache.raft.client.RaftClientRequestSender;
-import org.apache.raft.client.impl.RaftClientImpl;
-import org.apache.raft.conf.RaftProperties;
-import org.apache.raft.protocol.*;
-import org.apache.raft.server.simulation.RequestHandler;
-import org.apache.raft.server.storage.RaftLog;
-import org.apache.raft.util.RaftUtils;
-import org.junit.Assert;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.mockito.internal.util.reflection.Whitebox;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static java.util.Arrays.asList;
-import static org.apache.raft.MiniRaftCluster.logSyncDelay;
-import static org.apache.raft.server.RaftServerConstants.DEFAULT_SEQNUM;
-import static org.apache.raft.server.RaftServerTestUtil.waitAndCheckNewConf;
-import static 
org.apache.raft.shaded.proto.RaftProtos.LogEntryProto.LogEntryBodyCase.CONFIGURATIONENTRY;
-
-public abstract class RaftReconfigurationBaseTest {
-  static {
-    RaftUtils.setLogLevel(RaftServer.LOG, Level.DEBUG);
-    RaftUtils.setLogLevel(RequestHandler.LOG, Level.DEBUG);
-    RaftUtils.setLogLevel(RaftClient.LOG, Level.DEBUG);
-  }
-  static final Logger LOG = 
LoggerFactory.getLogger(RaftReconfigurationBaseTest.class);
-
-  protected static final RaftProperties prop = new RaftProperties();
-
-  @BeforeClass
-  public static void setup() {
-    // set a small gap for tests
-    prop.setInt(RaftServerConfigKeys.RAFT_SERVER_STAGING_CATCHUP_GAP_KEY, 10);
-  }
-
-  public abstract MiniRaftCluster getCluster(int peerNum) throws IOException;
-
-  private static int getStagingGap() {
-    return 
prop.getInt(RaftServerConfigKeys.RAFT_SERVER_STAGING_CATCHUP_GAP_KEY,
-        RaftServerConfigKeys.RAFT_SERVER_STAGING_CATCHUP_GAP_DEFAULT);
-  }
-
-  /**
-   * add 2 new peers (3 peers -> 5 peers), no leader change
-   */
-  @Test
-  public void testAddPeers() throws Exception {
-    LOG.info("Start testAddPeers");
-    MiniRaftCluster cluster = getCluster(3);
-    cluster.start();
-    try {
-      RaftTestUtil.waitForLeader(cluster);
-
-      // add new peers
-      RaftPeer[] allPeers = cluster.addNewPeers(2, true).allPeersInNewConf;
-
-      // trigger setConfiguration
-      SetConfigurationRequest request = new SetConfigurationRequest("client",
-          cluster.getLeader().getId(), DEFAULT_SEQNUM, allPeers);
-      LOG.info("Start changing the configuration: {}", request);
-      cluster.getLeader().setConfiguration(request);
-
-      // wait for the new configuration to take effect
-      waitAndCheckNewConf(cluster, allPeers, 0, null);
-    } finally {
-      cluster.shutdown();
-    }
-  }
-
-  /**
-   * remove 2 peers (5 peers -> 3 peers), no leader change
-   */
-  @Test
-  public void testRemovePeers() throws Exception {
-    LOG.info("Start testRemovePeers");
-    MiniRaftCluster cluster = getCluster(5);
-    cluster.start();
-    try {
-      RaftTestUtil.waitForLeader(cluster);
-
-      // remove peers, leader still included in the new conf
-      RaftPeer[] allPeers = cluster
-          .removePeers(2, false, Collections.emptyList()).allPeersInNewConf;
-
-      // trigger setConfiguration
-      SetConfigurationRequest request = new SetConfigurationRequest("client",
-          cluster.getLeader().getId(), DEFAULT_SEQNUM, allPeers);
-      LOG.info("Start changing the configuration: {}", request);
-      cluster.getLeader().setConfiguration(request);
-
-      // wait for the new configuration to take effect
-      waitAndCheckNewConf(cluster, allPeers, 2, null);
-    } finally {
-      cluster.shutdown();
-    }
-  }
-
-  /**
-   * 5 peers -> 5 peers, remove 2 old, add 2 new, no leader change
-   */
-  @Test
-  public void testAddRemovePeers() throws Exception {
-    LOG.info("Start testAddRemovePeers");
-    testAddRemovePeers(false);
-  }
-
-  @Test
-  public void testLeaderStepDown() throws Exception {
-    LOG.info("Start testLeaderStepDown");
-    testAddRemovePeers(true);
-  }
-
-  private void testAddRemovePeers(boolean leaderStepdown) throws Exception {
-    MiniRaftCluster cluster = getCluster(5);
-    cluster.start();
-    try {
-      RaftTestUtil.waitForLeader(cluster);
-
-      PeerChanges change = cluster.addNewPeers(2, true);
-      RaftPeer[] allPeers = cluster.removePeers(2, leaderStepdown,
-          asList(change.newPeers)).allPeersInNewConf;
-
-      // trigger setConfiguration
-      SetConfigurationRequest request = new SetConfigurationRequest("client",
-          cluster.getLeader().getId(), DEFAULT_SEQNUM, allPeers);
-      LOG.info("Start changing the configuration: {}", request);
-      cluster.getLeader().setConfiguration(request);
-
-      // wait for the new configuration to take effect
-      waitAndCheckNewConf(cluster, allPeers, 2, null);
-    } finally {
-      cluster.shutdown();
-    }
-  }
-
-  @Test(timeout = 30000)
-  public void testReconfTwice() throws Exception {
-    LOG.info("Start testReconfTwice");
-    final MiniRaftCluster cluster = getCluster(3);
-    cluster.start();
-    try {
-      RaftTestUtil.waitForLeader(cluster);
-      final String leaderId = cluster.getLeader().getId();
-      final RaftClient client = cluster.createClient("client", leaderId);
-
-      // submit some msgs before reconf
-      for (int i = 0; i < getStagingGap() * 2; i++) {
-        RaftClientReply reply = client.send(new SimpleMessage("m" + i));
-        Assert.assertTrue(reply.isSuccess());
-      }
-
-      final AtomicBoolean reconf1 = new AtomicBoolean(false);
-      final AtomicBoolean reconf2 = new AtomicBoolean(false);
-      final AtomicReference<RaftPeer[]> finalPeers = new 
AtomicReference<>(null);
-      final AtomicReference<RaftPeer[]> deadPeers = new 
AtomicReference<>(null);
-      CountDownLatch latch = new CountDownLatch(1);
-      Thread clientThread = new Thread(() -> {
-        try {
-          PeerChanges c1 = cluster.addNewPeers(2, true);
-          LOG.info("Start changing the configuration: {}",
-              asList(c1.allPeersInNewConf));
-
-          RaftClientReply reply = 
client.setConfiguration(c1.allPeersInNewConf);
-          reconf1.set(reply.isSuccess());
-
-          PeerChanges c2 = cluster.removePeers(2, true, asList(c1.newPeers));
-          finalPeers.set(c2.allPeersInNewConf);
-          deadPeers.set(c2.removedPeers);
-
-          LOG.info("Start changing the configuration again: {}",
-              asList(c2.allPeersInNewConf));
-          reply = client.setConfiguration(c2.allPeersInNewConf);
-          reconf2.set(reply.isSuccess());
-
-          latch.countDown();
-          client.close();
-        } catch (IOException ignored) {
-        }
-      });
-      clientThread.start();
-
-      latch.await();
-      Assert.assertTrue(reconf1.get());
-      Assert.assertTrue(reconf2.get());
-      waitAndCheckNewConf(cluster, finalPeers.get(), 2, null);
-
-      // check configuration manager's internal state
-      // each reconf will generate two configurations: (old, new) and (new)
-      cluster.getServers().stream().filter(RaftServer::isAlive)
-          .forEach(server -> {
-        ConfigurationManager confManager =
-            (ConfigurationManager) Whitebox.getInternalState(server.getState(),
-                "configurationManager");
-        // each reconf will generate two configurations: (old, new) and (new)
-        Assert.assertEquals(5, confManager.numOfConf());
-      });
-    } finally {
-      cluster.shutdown();
-    }
-  }
-
-  @Test
-  public void testReconfTimeout() throws Exception {
-    LOG.info("Start testReconfTimeout");
-    // originally 3 peers
-    final MiniRaftCluster cluster = getCluster(3);
-    cluster.start();
-    try {
-      RaftTestUtil.waitForLeader(cluster);
-      final String leaderId = cluster.getLeader().getId();
-      final RaftClient client = cluster.createClient("client", leaderId);
-
-      PeerChanges c1 = cluster.addNewPeers(2, false);
-
-      LOG.info("Start changing the configuration: {}",
-          asList(c1.allPeersInNewConf));
-      Assert.assertFalse(cluster.getLeader().getRaftConf().isTransitional());
-
-      final RaftClientRequestSender sender = 
((RaftClientImpl)client).getRequestSender();
-      final SetConfigurationRequest request = new SetConfigurationRequest(
-          "client", leaderId, DEFAULT_SEQNUM, c1.allPeersInNewConf);
-      try {
-        sender.sendRequest(request);
-        Assert.fail("did not get expected exception");
-      } catch (IOException e) {
-        Assert.assertTrue("Got exception " + e,
-            e instanceof ReconfigurationTimeoutException);
-      }
-
-      // the two new peers have not started yet, the bootstrapping must timeout
-      LOG.info(cluster.printServers());
-
-      // resend the same request, make sure the server has correctly reset its
-      // state so that we still get timeout instead of in-progress exception
-      try {
-        sender.sendRequest(request);
-        Assert.fail("did not get expected exception");
-      } catch (IOException e) {
-        Assert.assertTrue("Got exception " + e,
-            e instanceof ReconfigurationTimeoutException);
-      }
-
-      // start the two new peers
-      LOG.info("Start new peers");
-      for (RaftPeer np : c1.newPeers) {
-        cluster.startServer(np.getId());
-      }
-      
Assert.assertTrue(client.setConfiguration(c1.allPeersInNewConf).isSuccess());
-      client.close();
-    } finally {
-      cluster.shutdown();
-    }
-  }
-
-  @Test
-  public void testBootstrapReconf() throws Exception {
-    LOG.info("Start testBootstrapReconf");
-    // originally 3 peers
-    final MiniRaftCluster cluster = getCluster(3);
-    cluster.start();
-    try {
-      RaftTestUtil.waitForLeader(cluster);
-      final String leaderId = cluster.getLeader().getId();
-      final RaftClient client = cluster.createClient("client", leaderId);
-
-      // submit some msgs before reconf
-      for (int i = 0; i < getStagingGap() * 2; i++) {
-        RaftClientReply reply = client.send(new SimpleMessage("m" + i));
-        Assert.assertTrue(reply.isSuccess());
-      }
-
-      PeerChanges c1 = cluster.addNewPeers(2, true);
-      LOG.info("Start changing the configuration: {}",
-          asList(c1.allPeersInNewConf));
-      final AtomicReference<Boolean> success = new AtomicReference<>();
-
-      Thread clientThread = new Thread(() -> {
-        try {
-          RaftClientReply reply = 
client.setConfiguration(c1.allPeersInNewConf);
-          success.set(reply.isSuccess());
-          client.close();
-        } catch (IOException ioe) {
-          LOG.error("FAILED", ioe);
-        }
-      });
-      clientThread.start();
-
-      Thread.sleep(5000);
-      LOG.info(cluster.printServers());
-      assertSuccess(success);
-
-      final RaftLog leaderLog = cluster.getLeader().getState().getLog();
-      for (RaftPeer newPeer : c1.newPeers) {
-        Assert.assertArrayEquals(leaderLog.getEntries(0, Long.MAX_VALUE),
-            cluster.getServer(newPeer.getId()).getState().getLog()
-                .getEntries(0, Long.MAX_VALUE));
-      }
-    } finally {
-      cluster.shutdown();
-    }
-  }
-
-  /**
-   * kill the leader before reconfiguration finishes. Make sure the client 
keeps
-   * retrying.
-   */
-  @Test
-  public void testKillLeaderDuringReconf() throws Exception {
-    LOG.info("Start testKillLeaderDuringReconf");
-    // originally 3 peers
-    final MiniRaftCluster cluster = getCluster(3);
-    cluster.start();
-    try {
-      RaftTestUtil.waitForLeader(cluster);
-      final String leaderId = cluster.getLeader().getId();
-      final RaftClient client = cluster.createClient("client", leaderId);
-
-      PeerChanges c1 = cluster.addNewPeers(2, false);
-      PeerChanges c2 = cluster.removePeers(2, false, asList(c1.newPeers));
-
-      LOG.info("Start changing the configuration: {}",
-          asList(c2.allPeersInNewConf));
-      final AtomicReference<Boolean> success = new AtomicReference<>();
-      final AtomicBoolean clientRunning = new AtomicBoolean(true);
-      Thread clientThread = new Thread(() -> {
-        try {
-          boolean r = false;
-          while (clientRunning.get() && !r) {
-            r = client.setConfiguration(c2.allPeersInNewConf).isSuccess();
-          }
-          success.set(r);
-          client.close();
-        } catch (IOException ignored) {
-        }
-      });
-      clientThread.start();
-
-      // the leader cannot generate the (old, new) conf, and it will keep
-      // bootstrapping the 2 new peers since they have not started yet
-      LOG.info(cluster.printServers());
-      Assert.assertFalse(cluster.getLeader().getRaftConf().isTransitional());
-
-      // only the first empty entry got committed
-      final long committedIndex = cluster.getLeader().getState().getLog()
-          .getLastCommittedIndex();
-      Assert.assertTrue("committedIndex is " + committedIndex,
-          committedIndex <= 1);
-
-      LOG.info("kill the current leader");
-      final String oldLeaderId = RaftTestUtil.waitAndKillLeader(cluster, true);
-      LOG.info("start the two new peers: {}", Arrays.asList(c1.newPeers));
-      for (RaftPeer np : c1.newPeers) {
-        cluster.startServer(np.getId());
-      }
-
-      Thread.sleep(3000);
-      // the client should get the NotLeaderException from the first leader, 
and
-      // will retry the same setConfiguration request
-      waitAndCheckNewConf(cluster, c2.allPeersInNewConf, 2,
-          Collections.singletonList(oldLeaderId));
-      clientRunning.set(false);
-      //Assert.assertTrue(success.get());
-    } finally {
-      cluster.shutdown();
-    }
-  }
-
-  static void assertSuccess(final AtomicReference<Boolean> success) {
-    final String s = "success=" + success;
-    Assert.assertNotNull(s, success.get());
-    Assert.assertTrue(s, success.get());
-  }
-
-  /**
-   * When a request's new configuration is the same with the current one, make
-   * sure we return success immediately and no log entry is recorded.
-   */
-  @Test
-  public void testNoChangeRequest() throws Exception {
-    LOG.info("Start testNoChangeRequest");
-    // originally 3 peers
-    final MiniRaftCluster cluster = getCluster(3);
-    try {
-      cluster.start();
-      RaftTestUtil.waitForLeader(cluster);
-
-      final String leaderId = cluster.getLeader().getId();
-      final RaftClient client = cluster.createClient("client", leaderId);
-      client.send(new SimpleMessage("m"));
-
-      final long committedIndex = cluster.getLeader().getState().getLog()
-          .getLastCommittedIndex();
-      final RaftConfiguration confBefore = cluster.getLeader().getRaftConf();
-
-      // no real configuration change in the request
-      RaftClientReply reply = client.setConfiguration(cluster.getPeers()
-          .toArray(new RaftPeer[0]));
-      Assert.assertTrue(reply.isSuccess());
-      Assert.assertEquals(committedIndex, cluster.getLeader().getState()
-          .getLog().getLastCommittedIndex());
-      Assert.assertSame(confBefore, cluster.getLeader().getRaftConf());
-      client.close();
-    } finally {
-      cluster.shutdown();
-    }
-  }
-
-  /**
-   * Make sure a setConfiguration request is rejected if a configuration change
-   * is still in progress (i.e., has not been committed yet).
-   */
-  @Test
-  public void testOverlappedSetConfRequests() throws Exception {
-    LOG.info("Start testOverlappedSetConfRequests");
-    // originally 3 peers
-    final MiniRaftCluster cluster = getCluster(3);
-    try {
-      cluster.start();
-      RaftTestUtil.waitForLeader(cluster);
-
-      final String leaderId = cluster.getLeader().getId();
-
-      RaftPeer[] newPeers = cluster.addNewPeers(2, true).allPeersInNewConf;
-
-      // delay every peer's logSync so that the setConf request is delayed
-      cluster.getPeers()
-          .forEach(peer -> logSyncDelay.setDelayMs(peer.getId(), 1000));
-
-      final CountDownLatch latch = new CountDownLatch(1);
-      final RaftPeer[] peersInRequest2 = cluster.getPeers().toArray(new 
RaftPeer[0]);
-      AtomicBoolean caughtException = new AtomicBoolean(false);
-      new Thread(() -> {
-        try(final RaftClient client2 = cluster.createClient("client2", 
leaderId)) {
-          latch.await();
-          LOG.info("client2 starts to change conf");
-          final RaftClientRequestSender sender2 = 
((RaftClientImpl)client2).getRequestSender();
-          sender2.sendRequest(new SetConfigurationRequest(
-              "client2", leaderId, DEFAULT_SEQNUM, peersInRequest2));
-        } catch (ReconfigurationInProgressException e) {
-          caughtException.set(true);
-        } catch (Exception e) {
-          LOG.warn("Got unexpected exception when client2 changes conf", e);
-        }
-      }).start();
-
-      AtomicBoolean confChanged = new AtomicBoolean(false);
-      new Thread(() -> {
-        try(final RaftClient client1 = cluster.createClient("client1", 
leaderId)) {
-          LOG.info("client1 starts to change conf");
-          confChanged.set(client1.setConfiguration(newPeers).isSuccess());
-        } catch (IOException e) {
-          LOG.warn("Got unexpected exception when client1 changes conf", e);
-        }
-      }).start();
-      Thread.sleep(100);
-      latch.countDown();
-
-      for (int i = 0; i < 10 && !confChanged.get(); i++) {
-        Thread.sleep(1000);
-      }
-      Assert.assertTrue(confChanged.get());
-      Assert.assertTrue(caughtException.get());
-    } finally {
-      logSyncDelay.clear();
-      cluster.shutdown();
-    }
-  }
-
-  /**
-   * Test a scenario where the follower truncates its log entries which causes
-   * configuration change.
-   */
-  @Test
-  public void testRevertConfigurationChange() throws Exception {
-    LOG.info("Start testRevertConfigurationChange");
-    // originally 3 peers
-    final MiniRaftCluster cluster = getCluster(5);
-    try {
-      cluster.start();
-      RaftTestUtil.waitForLeader(cluster);
-
-      final String leaderId = cluster.getLeader().getId();
-
-      final RaftLog log = cluster.getServer(leaderId).getState().getLog();
-      Thread.sleep(1000);
-      Assert.assertEquals(0, log.getLatestFlushedIndex());
-
-      // we block the incoming msg for the leader and block its requests to
-      // followers, so that we force the leader change and the old leader will
-      // not know
-      LOG.info("start blocking the leader");
-      BlockRequestHandlingInjection.getInstance().blockReplier(leaderId);
-      cluster.setBlockRequestsFrom(leaderId, true);
-
-      PeerChanges change = cluster.removePeers(1, false, new ArrayList<>());
-
-      AtomicBoolean gotNotLeader = new AtomicBoolean(false);
-      new Thread(() -> {
-        try(final RaftClient client = cluster.createClient("client1", 
leaderId)) {
-          LOG.info("client starts to change conf");
-          final RaftClientRequestSender sender = 
((RaftClientImpl)client).getRequestSender();
-          RaftClientReply reply = sender.sendRequest(new 
SetConfigurationRequest(
-              "client", leaderId, DEFAULT_SEQNUM, change.allPeersInNewConf));
-          if (reply.isNotLeader()) {
-            gotNotLeader.set(true);
-          }
-        } catch (IOException e) {
-          LOG.warn("Got unexpected exception when client1 changes conf", e);
-        }
-      }).start();
-
-      // wait till the old leader persist the new conf
-      for (int i = 0; i < 10 && log.getLatestFlushedIndex() < 1; i++) {
-        Thread.sleep(500);
-      }
-      Assert.assertEquals(1, log.getLatestFlushedIndex());
-      Assert.assertEquals(CONFIGURATIONENTRY,
-          log.getLastEntry().getLogEntryBodyCase());
-
-      // unblock the old leader
-      BlockRequestHandlingInjection.getInstance().unblockReplier(leaderId);
-      cluster.setBlockRequestsFrom(leaderId, false);
-
-      // the client should get NotLeaderException
-      for (int i = 0; i < 10 && !gotNotLeader.get(); i++) {
-        Thread.sleep(500);
-      }
-      Assert.assertTrue(gotNotLeader.get());
-
-      // the old leader should have truncated the setConf from the log
-      boolean newState = false;
-      for (int i = 0; i < 10 && !newState; i++) {
-        Thread.sleep(500);
-        newState = log.getLastCommittedIndex() == 1 &&
-            log.getLastEntry().getLogEntryBodyCase() != CONFIGURATIONENTRY;
-      }
-      Assert.assertTrue(newState);
-    } finally {
-      cluster.shutdown();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/RaftServerTestUtil.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/RaftServerTestUtil.java 
b/raft-server/src/test/java/org/apache/raft/server/RaftServerTestUtil.java
deleted file mode 100644
index d52e3ad..0000000
--- a/raft-server/src/test/java/org/apache/raft/server/RaftServerTestUtil.java
+++ /dev/null
@@ -1,67 +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.raft.server;
-
-import org.apache.raft.MiniRaftCluster;
-import org.apache.raft.RaftTestUtil;
-import org.apache.raft.protocol.RaftPeer;
-import org.junit.Assert;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Collection;
-
-public class RaftServerTestUtil {
-  static final Logger LOG = LoggerFactory.getLogger(RaftServerTestUtil.class);
-
-  public static void waitAndCheckNewConf(MiniRaftCluster cluster,
-      RaftPeer[] peers, int numOfRemovedPeers, Collection<String> deadPeers)
-      throws Exception {
-    final long sleepMs = cluster.getMaxTimeout() * (numOfRemovedPeers + 2);
-    RaftTestUtil.attempt(3, sleepMs,
-        () -> waitAndCheckNewConf(cluster, peers, deadPeers));
-  }
-  private static void waitAndCheckNewConf(MiniRaftCluster cluster,
-      RaftPeer[] peers, Collection<String> deadPeers)
-      throws Exception {
-    LOG.info(cluster.printServers());
-    Assert.assertNotNull(cluster.getLeader());
-
-    int numIncluded = 0;
-    int deadIncluded = 0;
-    final RaftConfiguration current = RaftConfiguration.newBuilder()
-        .setConf(peers).setLogEntryIndex(0).build();
-    for (RaftServer server : cluster.getServers()) {
-      if (deadPeers != null && deadPeers.contains(server.getId())) {
-        if (current.containsInConf(server.getId())) {
-          deadIncluded++;
-        }
-        continue;
-      }
-      if (current.containsInConf(server.getId())) {
-        numIncluded++;
-        Assert.assertTrue(server.getRaftConf().isStable());
-        Assert.assertTrue(server.getRaftConf().hasNoChange(peers));
-      } else {
-        Assert.assertFalse(server.getId() + " is still running: " + server,
-            server.isAlive());
-      }
-    }
-    Assert.assertEquals(peers.length, numIncluded + deadIncluded);
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/impl/BlockRequestHandlingInjection.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/impl/BlockRequestHandlingInjection.java
 
b/raft-server/src/test/java/org/apache/raft/server/impl/BlockRequestHandlingInjection.java
new file mode 100644
index 0000000..0980e93
--- /dev/null
+++ 
b/raft-server/src/test/java/org/apache/raft/server/impl/BlockRequestHandlingInjection.java
@@ -0,0 +1,84 @@
+/**
+ * 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.raft.server.impl;
+
+import org.apache.raft.RaftTestUtil;
+import org.apache.raft.util.CodeInjectionForTesting;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/** Inject code to block a server from handling incoming requests. */
+public class BlockRequestHandlingInjection implements 
CodeInjectionForTesting.Code {
+  private static final BlockRequestHandlingInjection INSTANCE =
+      new BlockRequestHandlingInjection();
+
+  static {
+    CodeInjectionForTesting.put(RaftServer.REQUEST_VOTE, INSTANCE);
+    CodeInjectionForTesting.put(RaftServer.APPEND_ENTRIES, INSTANCE);
+    CodeInjectionForTesting.put(RaftServer.INSTALL_SNAPSHOT, INSTANCE);
+  }
+
+  public static BlockRequestHandlingInjection getInstance() {
+    return INSTANCE;
+  }
+
+  private final Map<String, Boolean> requestors = new ConcurrentHashMap<>();
+  private final Map<String, Boolean> repliers = new ConcurrentHashMap<>();
+
+  private BlockRequestHandlingInjection() {}
+
+  public void blockRequestor(String requestor) {
+    requestors.put(requestor, true);
+  }
+
+  public void unblockRequestor(String requestor) {
+    requestors.remove(requestor);
+  }
+
+  public void blockReplier(String replier) {
+    repliers.put(replier, true);
+  }
+
+  public void unblockReplier(String replier) {
+    repliers.remove(replier);
+  }
+
+  public void unblockAll() {
+    requestors.clear();
+    repliers.clear();
+  }
+
+  @Override
+  public boolean execute(String localId, String remoteId, Object... args) {
+    if (shouldBlock(localId, remoteId)) {
+      try {
+        RaftTestUtil.block(() -> shouldBlock(localId, remoteId));
+        return true;
+      } catch (InterruptedException e) {
+        LOG.debug("Interrupted while blocking request handling from " + 
remoteId
+            + " to " + localId);
+      }
+    }
+    return false;
+  }
+
+  private boolean shouldBlock(String localId, String remoteId) {
+    return repliers.containsKey(localId) || requestors.containsKey(remoteId);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/impl/DelayLocalExecutionInjection.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/impl/DelayLocalExecutionInjection.java
 
b/raft-server/src/test/java/org/apache/raft/server/impl/DelayLocalExecutionInjection.java
new file mode 100644
index 0000000..26b89d8
--- /dev/null
+++ 
b/raft-server/src/test/java/org/apache/raft/server/impl/DelayLocalExecutionInjection.java
@@ -0,0 +1,67 @@
+/**
+ * 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.raft.server.impl;
+
+import org.apache.raft.RaftTestUtil;
+import org.apache.raft.util.CodeInjectionForTesting;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** Inject code to delay particular servers. */
+public class DelayLocalExecutionInjection implements 
CodeInjectionForTesting.Code {
+  private final Map<String, AtomicInteger> delays = new ConcurrentHashMap<>();
+
+  public DelayLocalExecutionInjection(String method) {
+    CodeInjectionForTesting.put(method, this);
+  }
+
+  public void clear() {
+    delays.clear();
+  }
+
+  public void setDelayMs(String id, int delayMs) {
+    AtomicInteger d = delays.get(id);
+    if (d == null) {
+      delays.put(id, d = new AtomicInteger());
+    }
+    d.set(delayMs);
+  }
+
+  public void removeDelay(String id) {
+    delays.remove(id);
+  }
+
+  @Override
+  public boolean execute(String localId, String remoteId, Object... args) {
+    final AtomicInteger d = delays.get(localId);
+    if (d == null) {
+      return false;
+    }
+    LOG.info("{} delay {} ms, args={}", localId, d.get(),
+        Arrays.toString(args));
+    try {
+      RaftTestUtil.delay(d::get);
+    } catch (InterruptedException e) {
+      LOG.debug("Interrupted while delaying " + localId);
+    }
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/impl/RaftReconfigurationBaseTest.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/impl/RaftReconfigurationBaseTest.java
 
b/raft-server/src/test/java/org/apache/raft/server/impl/RaftReconfigurationBaseTest.java
new file mode 100644
index 0000000..30f1e15
--- /dev/null
+++ 
b/raft-server/src/test/java/org/apache/raft/server/impl/RaftReconfigurationBaseTest.java
@@ -0,0 +1,577 @@
+/**
+ * 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.raft.server.impl;
+
+import org.apache.log4j.Level;
+import org.apache.raft.MiniRaftCluster;
+import org.apache.raft.MiniRaftCluster.PeerChanges;
+import org.apache.raft.RaftTestUtil;
+import org.apache.raft.RaftTestUtil.SimpleMessage;
+import org.apache.raft.client.RaftClient;
+import org.apache.raft.client.RaftClientRequestSender;
+import org.apache.raft.client.impl.RaftClientImpl;
+import org.apache.raft.conf.RaftProperties;
+import org.apache.raft.protocol.*;
+import org.apache.raft.server.RaftServerConfigKeys;
+import org.apache.raft.server.simulation.RequestHandler;
+import org.apache.raft.server.storage.RaftLog;
+import org.apache.raft.util.RaftUtils;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.internal.util.reflection.Whitebox;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static java.util.Arrays.asList;
+import static org.apache.raft.MiniRaftCluster.logSyncDelay;
+import static org.apache.raft.server.impl.RaftServerConstants.DEFAULT_SEQNUM;
+import static 
org.apache.raft.server.impl.RaftServerTestUtil.waitAndCheckNewConf;
+import static 
org.apache.raft.shaded.proto.RaftProtos.LogEntryProto.LogEntryBodyCase.CONFIGURATIONENTRY;
+
+public abstract class RaftReconfigurationBaseTest {
+  static {
+    RaftUtils.setLogLevel(RaftServer.LOG, Level.DEBUG);
+    RaftUtils.setLogLevel(RequestHandler.LOG, Level.DEBUG);
+    RaftUtils.setLogLevel(RaftClient.LOG, Level.DEBUG);
+  }
+  static final Logger LOG = 
LoggerFactory.getLogger(RaftReconfigurationBaseTest.class);
+
+  protected static final RaftProperties prop = new RaftProperties();
+
+  @BeforeClass
+  public static void setup() {
+    // set a small gap for tests
+    prop.setInt(RaftServerConfigKeys.RAFT_SERVER_STAGING_CATCHUP_GAP_KEY, 10);
+  }
+
+  public abstract MiniRaftCluster getCluster(int peerNum) throws IOException;
+
+  private static int getStagingGap() {
+    return 
prop.getInt(RaftServerConfigKeys.RAFT_SERVER_STAGING_CATCHUP_GAP_KEY,
+        RaftServerConfigKeys.RAFT_SERVER_STAGING_CATCHUP_GAP_DEFAULT);
+  }
+
+  /**
+   * add 2 new peers (3 peers -> 5 peers), no leader change
+   */
+  @Test
+  public void testAddPeers() throws Exception {
+    LOG.info("Start testAddPeers");
+    MiniRaftCluster cluster = getCluster(3);
+    cluster.start();
+    try {
+      RaftTestUtil.waitForLeader(cluster);
+
+      // add new peers
+      RaftPeer[] allPeers = cluster.addNewPeers(2, true).allPeersInNewConf;
+
+      // trigger setConfiguration
+      SetConfigurationRequest request = new SetConfigurationRequest("client",
+          cluster.getLeader().getId(), DEFAULT_SEQNUM, allPeers);
+      LOG.info("Start changing the configuration: {}", request);
+      cluster.getLeader().setConfiguration(request);
+
+      // wait for the new configuration to take effect
+      waitAndCheckNewConf(cluster, allPeers, 0, null);
+    } finally {
+      cluster.shutdown();
+    }
+  }
+
+  /**
+   * remove 2 peers (5 peers -> 3 peers), no leader change
+   */
+  @Test
+  public void testRemovePeers() throws Exception {
+    LOG.info("Start testRemovePeers");
+    MiniRaftCluster cluster = getCluster(5);
+    cluster.start();
+    try {
+      RaftTestUtil.waitForLeader(cluster);
+
+      // remove peers, leader still included in the new conf
+      RaftPeer[] allPeers = cluster
+          .removePeers(2, false, Collections.emptyList()).allPeersInNewConf;
+
+      // trigger setConfiguration
+      SetConfigurationRequest request = new SetConfigurationRequest("client",
+          cluster.getLeader().getId(), DEFAULT_SEQNUM, allPeers);
+      LOG.info("Start changing the configuration: {}", request);
+      cluster.getLeader().setConfiguration(request);
+
+      // wait for the new configuration to take effect
+      waitAndCheckNewConf(cluster, allPeers, 2, null);
+    } finally {
+      cluster.shutdown();
+    }
+  }
+
+  /**
+   * 5 peers -> 5 peers, remove 2 old, add 2 new, no leader change
+   */
+  @Test
+  public void testAddRemovePeers() throws Exception {
+    LOG.info("Start testAddRemovePeers");
+    testAddRemovePeers(false);
+  }
+
+  @Test
+  public void testLeaderStepDown() throws Exception {
+    LOG.info("Start testLeaderStepDown");
+    testAddRemovePeers(true);
+  }
+
+  private void testAddRemovePeers(boolean leaderStepdown) throws Exception {
+    MiniRaftCluster cluster = getCluster(5);
+    cluster.start();
+    try {
+      RaftTestUtil.waitForLeader(cluster);
+
+      PeerChanges change = cluster.addNewPeers(2, true);
+      RaftPeer[] allPeers = cluster.removePeers(2, leaderStepdown,
+          asList(change.newPeers)).allPeersInNewConf;
+
+      // trigger setConfiguration
+      SetConfigurationRequest request = new SetConfigurationRequest("client",
+          cluster.getLeader().getId(), DEFAULT_SEQNUM, allPeers);
+      LOG.info("Start changing the configuration: {}", request);
+      cluster.getLeader().setConfiguration(request);
+
+      // wait for the new configuration to take effect
+      waitAndCheckNewConf(cluster, allPeers, 2, null);
+    } finally {
+      cluster.shutdown();
+    }
+  }
+
+  @Test(timeout = 30000)
+  public void testReconfTwice() throws Exception {
+    LOG.info("Start testReconfTwice");
+    final MiniRaftCluster cluster = getCluster(3);
+    cluster.start();
+    try {
+      RaftTestUtil.waitForLeader(cluster);
+      final String leaderId = cluster.getLeader().getId();
+      final RaftClient client = cluster.createClient("client", leaderId);
+
+      // submit some msgs before reconf
+      for (int i = 0; i < getStagingGap() * 2; i++) {
+        RaftClientReply reply = client.send(new SimpleMessage("m" + i));
+        Assert.assertTrue(reply.isSuccess());
+      }
+
+      final AtomicBoolean reconf1 = new AtomicBoolean(false);
+      final AtomicBoolean reconf2 = new AtomicBoolean(false);
+      final AtomicReference<RaftPeer[]> finalPeers = new 
AtomicReference<>(null);
+      final AtomicReference<RaftPeer[]> deadPeers = new 
AtomicReference<>(null);
+      CountDownLatch latch = new CountDownLatch(1);
+      Thread clientThread = new Thread(() -> {
+        try {
+          PeerChanges c1 = cluster.addNewPeers(2, true);
+          LOG.info("Start changing the configuration: {}",
+              asList(c1.allPeersInNewConf));
+
+          RaftClientReply reply = 
client.setConfiguration(c1.allPeersInNewConf);
+          reconf1.set(reply.isSuccess());
+
+          PeerChanges c2 = cluster.removePeers(2, true, asList(c1.newPeers));
+          finalPeers.set(c2.allPeersInNewConf);
+          deadPeers.set(c2.removedPeers);
+
+          LOG.info("Start changing the configuration again: {}",
+              asList(c2.allPeersInNewConf));
+          reply = client.setConfiguration(c2.allPeersInNewConf);
+          reconf2.set(reply.isSuccess());
+
+          latch.countDown();
+          client.close();
+        } catch (IOException ignored) {
+        }
+      });
+      clientThread.start();
+
+      latch.await();
+      Assert.assertTrue(reconf1.get());
+      Assert.assertTrue(reconf2.get());
+      waitAndCheckNewConf(cluster, finalPeers.get(), 2, null);
+
+      // check configuration manager's internal state
+      // each reconf will generate two configurations: (old, new) and (new)
+      cluster.getServers().stream().filter(RaftServer::isAlive)
+          .forEach(server -> {
+        ConfigurationManager confManager =
+            (ConfigurationManager) Whitebox.getInternalState(server.getState(),
+                "configurationManager");
+        // each reconf will generate two configurations: (old, new) and (new)
+        Assert.assertEquals(5, confManager.numOfConf());
+      });
+    } finally {
+      cluster.shutdown();
+    }
+  }
+
+  @Test
+  public void testReconfTimeout() throws Exception {
+    LOG.info("Start testReconfTimeout");
+    // originally 3 peers
+    final MiniRaftCluster cluster = getCluster(3);
+    cluster.start();
+    try {
+      RaftTestUtil.waitForLeader(cluster);
+      final String leaderId = cluster.getLeader().getId();
+      final RaftClient client = cluster.createClient("client", leaderId);
+
+      PeerChanges c1 = cluster.addNewPeers(2, false);
+
+      LOG.info("Start changing the configuration: {}",
+          asList(c1.allPeersInNewConf));
+      Assert.assertFalse(cluster.getLeader().getRaftConf().isTransitional());
+
+      final RaftClientRequestSender sender = 
((RaftClientImpl)client).getRequestSender();
+      final SetConfigurationRequest request = new SetConfigurationRequest(
+          "client", leaderId, DEFAULT_SEQNUM, c1.allPeersInNewConf);
+      try {
+        sender.sendRequest(request);
+        Assert.fail("did not get expected exception");
+      } catch (IOException e) {
+        Assert.assertTrue("Got exception " + e,
+            e instanceof ReconfigurationTimeoutException);
+      }
+
+      // the two new peers have not started yet, the bootstrapping must timeout
+      LOG.info(cluster.printServers());
+
+      // resend the same request, make sure the server has correctly reset its
+      // state so that we still get timeout instead of in-progress exception
+      try {
+        sender.sendRequest(request);
+        Assert.fail("did not get expected exception");
+      } catch (IOException e) {
+        Assert.assertTrue("Got exception " + e,
+            e instanceof ReconfigurationTimeoutException);
+      }
+
+      // start the two new peers
+      LOG.info("Start new peers");
+      for (RaftPeer np : c1.newPeers) {
+        cluster.startServer(np.getId());
+      }
+      
Assert.assertTrue(client.setConfiguration(c1.allPeersInNewConf).isSuccess());
+      client.close();
+    } finally {
+      cluster.shutdown();
+    }
+  }
+
+  @Test
+  public void testBootstrapReconf() throws Exception {
+    LOG.info("Start testBootstrapReconf");
+    // originally 3 peers
+    final MiniRaftCluster cluster = getCluster(3);
+    cluster.start();
+    try {
+      RaftTestUtil.waitForLeader(cluster);
+      final String leaderId = cluster.getLeader().getId();
+      final RaftClient client = cluster.createClient("client", leaderId);
+
+      // submit some msgs before reconf
+      for (int i = 0; i < getStagingGap() * 2; i++) {
+        RaftClientReply reply = client.send(new SimpleMessage("m" + i));
+        Assert.assertTrue(reply.isSuccess());
+      }
+
+      PeerChanges c1 = cluster.addNewPeers(2, true);
+      LOG.info("Start changing the configuration: {}",
+          asList(c1.allPeersInNewConf));
+      final AtomicReference<Boolean> success = new AtomicReference<>();
+
+      Thread clientThread = new Thread(() -> {
+        try {
+          RaftClientReply reply = 
client.setConfiguration(c1.allPeersInNewConf);
+          success.set(reply.isSuccess());
+          client.close();
+        } catch (IOException ioe) {
+          LOG.error("FAILED", ioe);
+        }
+      });
+      clientThread.start();
+
+      Thread.sleep(5000);
+      LOG.info(cluster.printServers());
+      assertSuccess(success);
+
+      final RaftLog leaderLog = cluster.getLeader().getState().getLog();
+      for (RaftPeer newPeer : c1.newPeers) {
+        Assert.assertArrayEquals(leaderLog.getEntries(0, Long.MAX_VALUE),
+            cluster.getServer(newPeer.getId()).getState().getLog()
+                .getEntries(0, Long.MAX_VALUE));
+      }
+    } finally {
+      cluster.shutdown();
+    }
+  }
+
+  /**
+   * kill the leader before reconfiguration finishes. Make sure the client 
keeps
+   * retrying.
+   */
+  @Test
+  public void testKillLeaderDuringReconf() throws Exception {
+    LOG.info("Start testKillLeaderDuringReconf");
+    // originally 3 peers
+    final MiniRaftCluster cluster = getCluster(3);
+    cluster.start();
+    try {
+      RaftTestUtil.waitForLeader(cluster);
+      final String leaderId = cluster.getLeader().getId();
+      final RaftClient client = cluster.createClient("client", leaderId);
+
+      PeerChanges c1 = cluster.addNewPeers(2, false);
+      PeerChanges c2 = cluster.removePeers(2, false, asList(c1.newPeers));
+
+      LOG.info("Start changing the configuration: {}",
+          asList(c2.allPeersInNewConf));
+      final AtomicReference<Boolean> success = new AtomicReference<>();
+      final AtomicBoolean clientRunning = new AtomicBoolean(true);
+      Thread clientThread = new Thread(() -> {
+        try {
+          boolean r = false;
+          while (clientRunning.get() && !r) {
+            r = client.setConfiguration(c2.allPeersInNewConf).isSuccess();
+          }
+          success.set(r);
+          client.close();
+        } catch (IOException ignored) {
+        }
+      });
+      clientThread.start();
+
+      // the leader cannot generate the (old, new) conf, and it will keep
+      // bootstrapping the 2 new peers since they have not started yet
+      LOG.info(cluster.printServers());
+      Assert.assertFalse(cluster.getLeader().getRaftConf().isTransitional());
+
+      // only the first empty entry got committed
+      final long committedIndex = cluster.getLeader().getState().getLog()
+          .getLastCommittedIndex();
+      Assert.assertTrue("committedIndex is " + committedIndex,
+          committedIndex <= 1);
+
+      LOG.info("kill the current leader");
+      final String oldLeaderId = RaftTestUtil.waitAndKillLeader(cluster, true);
+      LOG.info("start the two new peers: {}", Arrays.asList(c1.newPeers));
+      for (RaftPeer np : c1.newPeers) {
+        cluster.startServer(np.getId());
+      }
+
+      Thread.sleep(3000);
+      // the client should get the NotLeaderException from the first leader, 
and
+      // will retry the same setConfiguration request
+      waitAndCheckNewConf(cluster, c2.allPeersInNewConf, 2,
+          Collections.singletonList(oldLeaderId));
+      clientRunning.set(false);
+      //Assert.assertTrue(success.get());
+    } finally {
+      cluster.shutdown();
+    }
+  }
+
+  static void assertSuccess(final AtomicReference<Boolean> success) {
+    final String s = "success=" + success;
+    Assert.assertNotNull(s, success.get());
+    Assert.assertTrue(s, success.get());
+  }
+
+  /**
+   * When a request's new configuration is the same with the current one, make
+   * sure we return success immediately and no log entry is recorded.
+   */
+  @Test
+  public void testNoChangeRequest() throws Exception {
+    LOG.info("Start testNoChangeRequest");
+    // originally 3 peers
+    final MiniRaftCluster cluster = getCluster(3);
+    try {
+      cluster.start();
+      RaftTestUtil.waitForLeader(cluster);
+
+      final String leaderId = cluster.getLeader().getId();
+      final RaftClient client = cluster.createClient("client", leaderId);
+      client.send(new SimpleMessage("m"));
+
+      final long committedIndex = cluster.getLeader().getState().getLog()
+          .getLastCommittedIndex();
+      final RaftConfiguration confBefore = cluster.getLeader().getRaftConf();
+
+      // no real configuration change in the request
+      RaftClientReply reply = client.setConfiguration(cluster.getPeers()
+          .toArray(new RaftPeer[0]));
+      Assert.assertTrue(reply.isSuccess());
+      Assert.assertEquals(committedIndex, cluster.getLeader().getState()
+          .getLog().getLastCommittedIndex());
+      Assert.assertSame(confBefore, cluster.getLeader().getRaftConf());
+      client.close();
+    } finally {
+      cluster.shutdown();
+    }
+  }
+
+  /**
+   * Make sure a setConfiguration request is rejected if a configuration change
+   * is still in progress (i.e., has not been committed yet).
+   */
+  @Test
+  public void testOverlappedSetConfRequests() throws Exception {
+    LOG.info("Start testOverlappedSetConfRequests");
+    // originally 3 peers
+    final MiniRaftCluster cluster = getCluster(3);
+    try {
+      cluster.start();
+      RaftTestUtil.waitForLeader(cluster);
+
+      final String leaderId = cluster.getLeader().getId();
+
+      RaftPeer[] newPeers = cluster.addNewPeers(2, true).allPeersInNewConf;
+
+      // delay every peer's logSync so that the setConf request is delayed
+      cluster.getPeers()
+          .forEach(peer -> logSyncDelay.setDelayMs(peer.getId(), 1000));
+
+      final CountDownLatch latch = new CountDownLatch(1);
+      final RaftPeer[] peersInRequest2 = cluster.getPeers().toArray(new 
RaftPeer[0]);
+      AtomicBoolean caughtException = new AtomicBoolean(false);
+      new Thread(() -> {
+        try(final RaftClient client2 = cluster.createClient("client2", 
leaderId)) {
+          latch.await();
+          LOG.info("client2 starts to change conf");
+          final RaftClientRequestSender sender2 = 
((RaftClientImpl)client2).getRequestSender();
+          sender2.sendRequest(new SetConfigurationRequest(
+              "client2", leaderId, DEFAULT_SEQNUM, peersInRequest2));
+        } catch (ReconfigurationInProgressException e) {
+          caughtException.set(true);
+        } catch (Exception e) {
+          LOG.warn("Got unexpected exception when client2 changes conf", e);
+        }
+      }).start();
+
+      AtomicBoolean confChanged = new AtomicBoolean(false);
+      new Thread(() -> {
+        try(final RaftClient client1 = cluster.createClient("client1", 
leaderId)) {
+          LOG.info("client1 starts to change conf");
+          confChanged.set(client1.setConfiguration(newPeers).isSuccess());
+        } catch (IOException e) {
+          LOG.warn("Got unexpected exception when client1 changes conf", e);
+        }
+      }).start();
+      Thread.sleep(100);
+      latch.countDown();
+
+      for (int i = 0; i < 10 && !confChanged.get(); i++) {
+        Thread.sleep(1000);
+      }
+      Assert.assertTrue(confChanged.get());
+      Assert.assertTrue(caughtException.get());
+    } finally {
+      logSyncDelay.clear();
+      cluster.shutdown();
+    }
+  }
+
+  /**
+   * Test a scenario where the follower truncates its log entries which causes
+   * configuration change.
+   */
+  @Test
+  public void testRevertConfigurationChange() throws Exception {
+    LOG.info("Start testRevertConfigurationChange");
+    // originally 3 peers
+    final MiniRaftCluster cluster = getCluster(5);
+    try {
+      cluster.start();
+      RaftTestUtil.waitForLeader(cluster);
+
+      final String leaderId = cluster.getLeader().getId();
+
+      final RaftLog log = cluster.getServer(leaderId).getState().getLog();
+      Thread.sleep(1000);
+      Assert.assertEquals(0, log.getLatestFlushedIndex());
+
+      // we block the incoming msg for the leader and block its requests to
+      // followers, so that we force the leader change and the old leader will
+      // not know
+      LOG.info("start blocking the leader");
+      BlockRequestHandlingInjection.getInstance().blockReplier(leaderId);
+      cluster.setBlockRequestsFrom(leaderId, true);
+
+      PeerChanges change = cluster.removePeers(1, false, new ArrayList<>());
+
+      AtomicBoolean gotNotLeader = new AtomicBoolean(false);
+      new Thread(() -> {
+        try(final RaftClient client = cluster.createClient("client1", 
leaderId)) {
+          LOG.info("client starts to change conf");
+          final RaftClientRequestSender sender = 
((RaftClientImpl)client).getRequestSender();
+          RaftClientReply reply = sender.sendRequest(new 
SetConfigurationRequest(
+              "client", leaderId, DEFAULT_SEQNUM, change.allPeersInNewConf));
+          if (reply.isNotLeader()) {
+            gotNotLeader.set(true);
+          }
+        } catch (IOException e) {
+          LOG.warn("Got unexpected exception when client1 changes conf", e);
+        }
+      }).start();
+
+      // wait till the old leader persist the new conf
+      for (int i = 0; i < 10 && log.getLatestFlushedIndex() < 1; i++) {
+        Thread.sleep(500);
+      }
+      Assert.assertEquals(1, log.getLatestFlushedIndex());
+      Assert.assertEquals(CONFIGURATIONENTRY,
+          log.getLastEntry().getLogEntryBodyCase());
+
+      // unblock the old leader
+      BlockRequestHandlingInjection.getInstance().unblockReplier(leaderId);
+      cluster.setBlockRequestsFrom(leaderId, false);
+
+      // the client should get NotLeaderException
+      for (int i = 0; i < 10 && !gotNotLeader.get(); i++) {
+        Thread.sleep(500);
+      }
+      Assert.assertTrue(gotNotLeader.get());
+
+      // the old leader should have truncated the setConf from the log
+      boolean newState = false;
+      for (int i = 0; i < 10 && !newState; i++) {
+        Thread.sleep(500);
+        newState = log.getLastCommittedIndex() == 1 &&
+            log.getLastEntry().getLogEntryBodyCase() != CONFIGURATIONENTRY;
+      }
+      Assert.assertTrue(newState);
+    } finally {
+      cluster.shutdown();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/impl/RaftServerTestUtil.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/impl/RaftServerTestUtil.java 
b/raft-server/src/test/java/org/apache/raft/server/impl/RaftServerTestUtil.java
new file mode 100644
index 0000000..b30ddc9
--- /dev/null
+++ 
b/raft-server/src/test/java/org/apache/raft/server/impl/RaftServerTestUtil.java
@@ -0,0 +1,67 @@
+/**
+ * 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.raft.server.impl;
+
+import org.apache.raft.MiniRaftCluster;
+import org.apache.raft.RaftTestUtil;
+import org.apache.raft.protocol.RaftPeer;
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+
+public class RaftServerTestUtil {
+  static final Logger LOG = LoggerFactory.getLogger(RaftServerTestUtil.class);
+
+  public static void waitAndCheckNewConf(MiniRaftCluster cluster,
+      RaftPeer[] peers, int numOfRemovedPeers, Collection<String> deadPeers)
+      throws Exception {
+    final long sleepMs = cluster.getMaxTimeout() * (numOfRemovedPeers + 2);
+    RaftTestUtil.attempt(3, sleepMs,
+        () -> waitAndCheckNewConf(cluster, peers, deadPeers));
+  }
+  private static void waitAndCheckNewConf(MiniRaftCluster cluster,
+      RaftPeer[] peers, Collection<String> deadPeers)
+      throws Exception {
+    LOG.info(cluster.printServers());
+    Assert.assertNotNull(cluster.getLeader());
+
+    int numIncluded = 0;
+    int deadIncluded = 0;
+    final RaftConfiguration current = RaftConfiguration.newBuilder()
+        .setConf(peers).setLogEntryIndex(0).build();
+    for (RaftServer server : cluster.getServers()) {
+      if (deadPeers != null && deadPeers.contains(server.getId())) {
+        if (current.containsInConf(server.getId())) {
+          deadIncluded++;
+        }
+        continue;
+      }
+      if (current.containsInConf(server.getId())) {
+        numIncluded++;
+        Assert.assertTrue(server.getRaftConf().isStable());
+        Assert.assertTrue(server.getRaftConf().hasNoChange(peers));
+      } else {
+        Assert.assertFalse(server.getId() + " is still running: " + server,
+            server.isAlive());
+      }
+    }
+    Assert.assertEquals(peers.length, numIncluded + deadIncluded);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/simulation/MiniRaftClusterWithSimulatedRpc.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/simulation/MiniRaftClusterWithSimulatedRpc.java
 
b/raft-server/src/test/java/org/apache/raft/server/simulation/MiniRaftClusterWithSimulatedRpc.java
index fd6f6fb..360fe1e 100644
--- 
a/raft-server/src/test/java/org/apache/raft/server/simulation/MiniRaftClusterWithSimulatedRpc.java
+++ 
b/raft-server/src/test/java/org/apache/raft/server/simulation/MiniRaftClusterWithSimulatedRpc.java
@@ -21,7 +21,7 @@ import org.apache.raft.MiniRaftCluster;
 import org.apache.raft.client.RaftClientRequestSender;
 import org.apache.raft.conf.RaftProperties;
 import org.apache.raft.protocol.RaftPeer;
-import org.apache.raft.server.RaftServer;
+import org.apache.raft.server.impl.RaftServer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/simulation/SimulatedServerRpc.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/simulation/SimulatedServerRpc.java
 
b/raft-server/src/test/java/org/apache/raft/server/simulation/SimulatedServerRpc.java
index bbb90f3..ed522d4 100644
--- 
a/raft-server/src/test/java/org/apache/raft/server/simulation/SimulatedServerRpc.java
+++ 
b/raft-server/src/test/java/org/apache/raft/server/simulation/SimulatedServerRpc.java
@@ -22,9 +22,9 @@ import org.apache.raft.protocol.RaftClientReply;
 import org.apache.raft.protocol.RaftClientRequest;
 import org.apache.raft.protocol.RaftPeer;
 import org.apache.raft.protocol.SetConfigurationRequest;
-import org.apache.raft.server.RaftServer;
-import org.apache.raft.server.RaftServerRpc;
-import org.apache.raft.server.RequestDispatcher;
+import org.apache.raft.server.impl.RaftServer;
+import org.apache.raft.server.impl.RaftServerRpc;
+import org.apache.raft.server.impl.RequestDispatcher;
 import org.apache.raft.shaded.proto.RaftProtos.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/simulation/TestRaftReconfigurationWithSimulatedRpc.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/simulation/TestRaftReconfigurationWithSimulatedRpc.java
 
b/raft-server/src/test/java/org/apache/raft/server/simulation/TestRaftReconfigurationWithSimulatedRpc.java
index 2fd71c7..b0eb456 100644
--- 
a/raft-server/src/test/java/org/apache/raft/server/simulation/TestRaftReconfigurationWithSimulatedRpc.java
+++ 
b/raft-server/src/test/java/org/apache/raft/server/simulation/TestRaftReconfigurationWithSimulatedRpc.java
@@ -18,7 +18,7 @@
 package org.apache.raft.server.simulation;
 
 import org.apache.raft.MiniRaftCluster;
-import org.apache.raft.server.RaftReconfigurationBaseTest;
+import org.apache.raft.server.impl.RaftReconfigurationBaseTest;
 
 import java.io.IOException;
 

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/simulation/TestRaftWithSimulatedRpc.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/simulation/TestRaftWithSimulatedRpc.java
 
b/raft-server/src/test/java/org/apache/raft/server/simulation/TestRaftWithSimulatedRpc.java
index c772f59..669226a 100644
--- 
a/raft-server/src/test/java/org/apache/raft/server/simulation/TestRaftWithSimulatedRpc.java
+++ 
b/raft-server/src/test/java/org/apache/raft/server/simulation/TestRaftWithSimulatedRpc.java
@@ -21,7 +21,7 @@ import org.apache.log4j.Level;
 import org.apache.raft.RaftBasicTests;
 import org.apache.raft.client.RaftClient;
 import org.apache.raft.conf.RaftProperties;
-import org.apache.raft.server.RaftServer;
+import org.apache.raft.server.impl.RaftServer;
 import org.apache.raft.util.RaftUtils;
 
 import java.io.IOException;

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftLogReadWrite.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftLogReadWrite.java
 
b/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftLogReadWrite.java
index 9fe15bc..fa17696 100644
--- 
a/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftLogReadWrite.java
+++ 
b/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftLogReadWrite.java
@@ -21,8 +21,8 @@ import org.apache.raft.RaftTestUtil;
 import org.apache.raft.RaftTestUtil.SimpleOperation;
 import org.apache.raft.conf.RaftProperties;
 import org.apache.raft.protocol.ChecksumException;
-import org.apache.raft.server.RaftServerConstants;
-import org.apache.raft.server.RaftServerConstants.StartupOption;
+import org.apache.raft.server.impl.RaftServerConstants;
+import org.apache.raft.server.impl.RaftServerConstants.StartupOption;
 import org.apache.raft.shaded.com.google.protobuf.CodedOutputStream;
 import org.apache.raft.shaded.proto.RaftProtos.LogEntryProto;
 import org.apache.raft.util.FileUtils;

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftLogSegment.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftLogSegment.java
 
b/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftLogSegment.java
index c0814e9..470f80f 100644
--- 
a/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftLogSegment.java
+++ 
b/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftLogSegment.java
@@ -21,7 +21,7 @@ import org.apache.raft.RaftTestUtil;
 import org.apache.raft.RaftTestUtil.SimpleOperation;
 import org.apache.raft.conf.RaftProperties;
 import org.apache.raft.server.RaftServerConfigKeys;
-import org.apache.raft.server.RaftServerConstants.StartupOption;
+import org.apache.raft.server.impl.RaftServerConstants.StartupOption;
 import org.apache.raft.shaded.proto.RaftProtos.LogEntryProto;
 import org.apache.raft.shaded.proto.RaftProtos.SMLogEntryProto;
 import org.apache.raft.util.FileUtils;
@@ -37,10 +37,8 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import static 
org.apache.raft.server.RaftServerConfigKeys.RAFT_LOG_SEGMENT_MAX_SIZE_KEY;
-import static 
org.apache.raft.server.RaftServerConfigKeys.RAFT_LOG_SEGMENT_PREALLOCATED_SIZE_KEY;
-import static 
org.apache.raft.server.RaftServerConfigKeys.RAFT_LOG_WRITE_BUFFER_SIZE_KEY;
-import static org.apache.raft.server.RaftServerConstants.INVALID_LOG_INDEX;
+import static org.apache.raft.server.RaftServerConfigKeys.*;
+import static 
org.apache.raft.server.impl.RaftServerConstants.INVALID_LOG_INDEX;
 import static org.apache.raft.server.storage.LogSegment.getEntrySize;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftStorage.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftStorage.java 
b/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftStorage.java
index ee5f481..1b14199 100644
--- 
a/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftStorage.java
+++ 
b/raft-server/src/test/java/org/apache/raft/server/storage/TestRaftStorage.java
@@ -20,8 +20,8 @@ package org.apache.raft.server.storage;
 import org.apache.raft.RaftTestUtil;
 import org.apache.raft.conf.RaftProperties;
 import org.apache.raft.io.nativeio.NativeIO;
-import org.apache.raft.server.RaftServerConstants.StartupOption;
 import org.apache.raft.server.RaftServerConfigKeys;
+import org.apache.raft.server.impl.RaftServerConstants.StartupOption;
 import org.apache.raft.server.protocol.TermIndex;
 import org.apache.raft.server.storage.RaftStorageDirectory.StorageState;
 import org.apache.raft.statemachine.SimpleStateMachineStorage;

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/server/storage/TestSegmentedRaftLog.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/server/storage/TestSegmentedRaftLog.java
 
b/raft-server/src/test/java/org/apache/raft/server/storage/TestSegmentedRaftLog.java
index 0f36a72..264ba8e 100644
--- 
a/raft-server/src/test/java/org/apache/raft/server/storage/TestSegmentedRaftLog.java
+++ 
b/raft-server/src/test/java/org/apache/raft/server/storage/TestSegmentedRaftLog.java
@@ -22,9 +22,9 @@ import org.apache.raft.MiniRaftCluster;
 import org.apache.raft.RaftTestUtil;
 import org.apache.raft.RaftTestUtil.SimpleOperation;
 import org.apache.raft.conf.RaftProperties;
-import org.apache.raft.server.ConfigurationManager;
 import org.apache.raft.server.RaftServerConfigKeys;
-import org.apache.raft.server.RaftServerConstants;
+import org.apache.raft.server.impl.ConfigurationManager;
+import org.apache.raft.server.impl.RaftServerConstants;
 import org.apache.raft.shaded.proto.RaftProtos.LogEntryProto;
 import org.apache.raft.util.FileUtils;
 import org.apache.raft.util.ProtoUtils;

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/statemachine/RaftSnapshotBaseTest.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/statemachine/RaftSnapshotBaseTest.java
 
b/raft-server/src/test/java/org/apache/raft/statemachine/RaftSnapshotBaseTest.java
index 4e7dce9..41ae9af 100644
--- 
a/raft-server/src/test/java/org/apache/raft/statemachine/RaftSnapshotBaseTest.java
+++ 
b/raft-server/src/test/java/org/apache/raft/statemachine/RaftSnapshotBaseTest.java
@@ -25,8 +25,8 @@ import org.apache.raft.client.RaftClient;
 import org.apache.raft.conf.RaftProperties;
 import org.apache.raft.protocol.RaftClientReply;
 import org.apache.raft.protocol.SetConfigurationRequest;
-import org.apache.raft.server.RaftServer;
-import org.apache.raft.server.RaftServerTestUtil;
+import org.apache.raft.server.impl.RaftServer;
+import org.apache.raft.server.impl.RaftServerTestUtil;
 import org.apache.raft.server.simulation.RequestHandler;
 import org.apache.raft.server.storage.RaftLog;
 import org.apache.raft.server.storage.RaftStorageDirectory;
@@ -48,7 +48,7 @@ import java.util.List;
 
 import static 
org.apache.raft.server.RaftServerConfigKeys.RAFT_SERVER_AUTO_SNAPSHOT_ENABLED_KEY;
 import static 
org.apache.raft.server.RaftServerConfigKeys.RAFT_SERVER_SNAPSHOT_TRIGGER_THRESHOLD_KEY;
-import static org.apache.raft.server.RaftServerConstants.DEFAULT_SEQNUM;
+import static org.apache.raft.server.impl.RaftServerConstants.DEFAULT_SEQNUM;
 
 public abstract class RaftSnapshotBaseTest {
   static {

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/statemachine/SimpleStateMachine4Testing.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/statemachine/SimpleStateMachine4Testing.java
 
b/raft-server/src/test/java/org/apache/raft/statemachine/SimpleStateMachine4Testing.java
index d745522..227ea58 100644
--- 
a/raft-server/src/test/java/org/apache/raft/statemachine/SimpleStateMachine4Testing.java
+++ 
b/raft-server/src/test/java/org/apache/raft/statemachine/SimpleStateMachine4Testing.java
@@ -25,7 +25,7 @@ import org.apache.raft.io.MD5Hash;
 import org.apache.raft.protocol.Message;
 import org.apache.raft.protocol.RaftClientReply;
 import org.apache.raft.protocol.RaftClientRequest;
-import org.apache.raft.server.RaftServerConstants;
+import org.apache.raft.server.impl.RaftServerConstants;
 import org.apache.raft.server.protocol.TermIndex;
 import org.apache.raft.server.storage.LogInputStream;
 import org.apache.raft.server.storage.LogOutputStream;

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/c36810ed/raft-server/src/test/java/org/apache/raft/statemachine/TestStateMachine.java
----------------------------------------------------------------------
diff --git 
a/raft-server/src/test/java/org/apache/raft/statemachine/TestStateMachine.java 
b/raft-server/src/test/java/org/apache/raft/statemachine/TestStateMachine.java
index ad606bf..c9dd99c 100644
--- 
a/raft-server/src/test/java/org/apache/raft/statemachine/TestStateMachine.java
+++ 
b/raft-server/src/test/java/org/apache/raft/statemachine/TestStateMachine.java
@@ -24,8 +24,8 @@ import org.apache.raft.client.RaftClient;
 import org.apache.raft.conf.RaftProperties;
 import org.apache.raft.protocol.Message;
 import org.apache.raft.protocol.RaftClientRequest;
-import org.apache.raft.server.RaftServer;
 import org.apache.raft.server.RaftServerConfigKeys;
+import org.apache.raft.server.impl.RaftServer;
 import org.apache.raft.server.simulation.MiniRaftClusterWithSimulatedRpc;
 import org.apache.raft.shaded.proto.RaftProtos.SMLogEntryProto;
 import org.apache.raft.util.RaftUtils;

Reply via email to