Repository: incubator-ratis
Updated Branches:
  refs/heads/master 86383d689 -> 298c1a2de


RATIS-104. Add an RPC to get server information. Contributed by Chen Liang


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

Branch: refs/heads/master
Commit: 298c1a2de173c0e2284fc08ef2f953a5fee86e2d
Parents: 86383d6
Author: Tsz-Wo Nicholas Sze <[email protected]>
Authored: Tue Sep 12 12:29:39 2017 +0800
Committer: Tsz-Wo Nicholas Sze <[email protected]>
Committed: Tue Sep 12 12:29:39 2017 +0800

----------------------------------------------------------------------
 .../org/apache/ratis/client/RaftClient.java     |   3 +
 .../ratis/client/impl/ClientProtoUtils.java     |  45 ++++++++
 .../ratis/client/impl/RaftClientImpl.java       |   9 ++
 .../protocol/AdminAsynchronousProtocol.java     |   3 +
 .../apache/ratis/protocol/AdminProtocol.java    |   2 +
 .../org/apache/ratis/protocol/RaftGroup.java    |   7 +-
 .../ratis/protocol/ServerInformationReply.java  |  33 ++++++
 .../ratis/protocol/ServerInformatonRequest.java |  12 +++
 .../org/apache/ratis/grpc/RaftGrpcUtil.java     |  10 +-
 .../apache/ratis/grpc/client/GrpcClientRpc.java |   7 ++
 .../grpc/client/RaftClientProtocolClient.java   |   7 ++
 .../grpc/client/RaftClientProtocolService.java  |   3 +-
 .../ratis/grpc/server/AdminProtocolService.java |  14 ++-
 .../grpc/TestServerInformationWithGrpc.java     |  25 +++++
 ...nedClientProtocolClientSideTranslatorPB.java |  40 +++++--
 ...nedClientProtocolServerSideTranslatorPB.java |  16 +++
 .../ratis/hadooprpc/client/HadoopClientRpc.java |   2 +
 .../TestServerInformationWithHadoopRpc.java     |   8 ++
 .../ratis/netty/client/NettyClientRpc.java      |  15 ++-
 .../ratis/netty/server/NettyRpcService.java     |  10 ++
 .../netty/TestServerInformationWithNetty.java   |   8 ++
 ratis-proto-shaded/src/main/proto/GRpc.proto    |   3 +
 ratis-proto-shaded/src/main/proto/Hadoop.proto  |   3 +
 ratis-proto-shaded/src/main/proto/Netty.proto   |   4 +-
 ratis-proto-shaded/src/main/proto/Raft.proto    |  10 ++
 .../ratis/server/impl/RaftServerImpl.java       |  10 +-
 .../ratis/server/impl/RaftServerProxy.java      |  27 +++++
 .../server/impl/ServerInformationBaseTest.java  | 103 +++++++++++++++++++
 .../server/simulation/SimulatedServerRpc.java   |   3 +
 .../TestServerInformationWithSimulatedRpc.java  |  25 +++++
 30 files changed, 441 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java
----------------------------------------------------------------------
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java 
b/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java
index 0effc4e..c682350 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java
@@ -54,6 +54,9 @@ public interface RaftClient extends Closeable {
   /** Send reinitialize request to the given server (not the raft service). */
   RaftClientReply reinitialize(RaftGroup newGroup, RaftPeerId server) throws 
IOException;
 
+  /** Send serverInformation request to the given server.*/
+  RaftClientReply serverInformation(RaftPeerId server) throws IOException;
+
   /** @return a {@link Builder}. */
   static Builder newBuilder() {
     return new Builder();

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/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 ea8c5ac..053d952 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
@@ -23,7 +23,9 @@ import org.apache.ratis.shaded.proto.RaftProtos.*;
 import org.apache.ratis.util.ProtoUtils;
 import org.apache.ratis.util.ReflectionUtils;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 import static 
org.apache.ratis.shaded.proto.RaftProtos.RaftClientReplyProto.ExceptionDetailsCase.NOTLEADEREXCEPTION;
 import static 
org.apache.ratis.shaded.proto.RaftProtos.RaftClientReplyProto.ExceptionDetailsCase.STATEMACHINEEXCEPTION;
@@ -130,6 +132,21 @@ public class ClientProtoUtils {
     return b.build();
   }
 
+  public static ServerInformationReplyProto toServerInformationReplyProto(
+      ServerInformationReply reply) {
+    final ServerInformationReplyProto.Builder b =
+        ServerInformationReplyProto.newBuilder();
+    if (reply != null) {
+      
b.setRpcReply(toRaftRpcReplyProtoBuilder(reply.getClientId().toByteString(),
+          reply.getServerId().toByteString(), reply.getRaftGroupId(),
+          reply.getCallId(), reply.isSuccess()));
+      if (reply.getRaftGroupId() != null) {
+        b.setGroup(ProtoUtils.toRaftGroupProtoBuilder(reply.getGroup()));
+      }
+    }
+    return b.build();
+  }
+
   public static RaftClientReply toRaftClientReply(
       RaftClientReplyProto replyProto) {
     final RaftRpcReplyProto rp = replyProto.getRpcReply();
@@ -155,6 +172,17 @@ public class ClientProtoUtils {
         toMessage(replyProto.getMessage()), e);
   }
 
+  public static ServerInformationReply toServerInformationReply(
+      ServerInformationReplyProto replyProto) {
+    final RaftRpcReplyProto rp = replyProto.getRpcReply();
+    ClientId clientId = ClientId.valueOf(rp.getRequestorId());
+    final RaftGroupId groupId = ProtoUtils.toRaftGroupId(rp.getRaftGroupId());
+    final RaftGroup raftGroup = ProtoUtils.toRaftGroup(replyProto.getGroup());
+    return new ServerInformationReply(clientId, 
RaftPeerId.valueOf(rp.getReplyId()),
+        groupId, rp.getCallId(), rp.getSuccess(), null,
+        null, raftGroup);
+  }
+
   private static StateMachineException wrapStateMachineException(
       RaftPeerId serverId, String className, String errorMsg,
       ByteString stackTraceBytes) {
@@ -220,6 +248,16 @@ public class ClientProtoUtils {
         ProtoUtils.toRaftGroup(p.getGroup()));
   }
 
+  public static ServerInformatonRequest toServerInformationRequest(
+      ServerInformationRequestProto p) {
+    final RaftRpcRequestProto m = p.getRpcRequest();
+    return new ServerInformatonRequest(
+        ClientId.valueOf(m.getRequestorId()),
+        RaftPeerId.valueOf(m.getReplyId()),
+        ProtoUtils.toRaftGroupId(m.getRaftGroupId()),
+        m.getCallId());
+  }
+
   public static ReinitializeRequestProto toReinitializeRequestProto(
       ReinitializeRequest request) {
     return ReinitializeRequestProto.newBuilder()
@@ -227,4 +265,11 @@ public class ClientProtoUtils {
         .setGroup(ProtoUtils.toRaftGroupProtoBuilder(request.getGroup()))
         .build();
   }
+
+  public static ServerInformationRequestProto toServerInformationRequestProto(
+      ServerInformatonRequest request) {
+    return ServerInformationRequestProto.newBuilder()
+        .setRpcRequest(toRaftRpcRequestProtoBuilder(request))
+        .build();
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/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 c25b9e0..30baee6 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
@@ -110,6 +110,15 @@ final class RaftClientImpl implements RaftClient {
         clientId, server, groupId, callId, newGroup));
   }
 
+  @Override
+  public RaftClientReply serverInformation(RaftPeerId server)
+      throws IOException {
+    Objects.requireNonNull(server, "server == null");
+
+    return sendRequest(new ServerInformatonRequest(clientId, server,
+        groupId, nextCallId()));
+  }
+
   private void addServers(Stream<RaftPeer> peersInNewConf) {
     clientRpc.addServers(
         peersInNewConf.filter(p -> !peers.contains(p))::iterator);

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-common/src/main/java/org/apache/ratis/protocol/AdminAsynchronousProtocol.java
----------------------------------------------------------------------
diff --git 
a/ratis-common/src/main/java/org/apache/ratis/protocol/AdminAsynchronousProtocol.java
 
b/ratis-common/src/main/java/org/apache/ratis/protocol/AdminAsynchronousProtocol.java
index 663751b..fd8444d 100644
--- 
a/ratis-common/src/main/java/org/apache/ratis/protocol/AdminAsynchronousProtocol.java
+++ 
b/ratis-common/src/main/java/org/apache/ratis/protocol/AdminAsynchronousProtocol.java
@@ -24,4 +24,7 @@ import java.util.concurrent.CompletableFuture;
 public interface AdminAsynchronousProtocol {
   CompletableFuture<RaftClientReply> reinitializeAsync(
       ReinitializeRequest request) throws IOException;
+
+  CompletableFuture<ServerInformationReply> getInfoAsync(
+      ServerInformatonRequest request) throws IOException;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-common/src/main/java/org/apache/ratis/protocol/AdminProtocol.java
----------------------------------------------------------------------
diff --git 
a/ratis-common/src/main/java/org/apache/ratis/protocol/AdminProtocol.java 
b/ratis-common/src/main/java/org/apache/ratis/protocol/AdminProtocol.java
index 0e7d6b6..6f81a8a 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/AdminProtocol.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/AdminProtocol.java
@@ -22,4 +22,6 @@ import java.io.IOException;
 /** For server administration. */
 public interface AdminProtocol {
   RaftClientReply reinitialize(ReinitializeRequest request) throws IOException;
+
+  ServerInformationReply getInfo(ServerInformatonRequest request) throws 
IOException;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroup.java
----------------------------------------------------------------------
diff --git 
a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroup.java 
b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroup.java
index d00bed5..1096518 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroup.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroup.java
@@ -19,10 +19,7 @@ package org.apache.ratis.protocol;
 
 import org.apache.ratis.util.Preconditions;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
 
 /**
  * Description of a raft group. It has a globally unique ID and a group of raft
@@ -42,7 +39,7 @@ public class RaftGroup {
     this(groupId, Arrays.asList(peers));
   }
 
-  public RaftGroup(RaftGroupId groupId, List<RaftPeer> peers) {
+  public RaftGroup(RaftGroupId groupId, Collection<RaftPeer> peers) {
     Preconditions.assertTrue(peers != null);
     this.groupId = groupId;
     this.peers = Collections.unmodifiableList(new ArrayList<>(peers));

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-common/src/main/java/org/apache/ratis/protocol/ServerInformationReply.java
----------------------------------------------------------------------
diff --git 
a/ratis-common/src/main/java/org/apache/ratis/protocol/ServerInformationReply.java
 
b/ratis-common/src/main/java/org/apache/ratis/protocol/ServerInformationReply.java
new file mode 100644
index 0000000..a01afe2
--- /dev/null
+++ 
b/ratis-common/src/main/java/org/apache/ratis/protocol/ServerInformationReply.java
@@ -0,0 +1,33 @@
+package org.apache.ratis.protocol;
+
+/**
+ * The response of server information request. Sent from server to client.
+ *
+ * TODO : currently, only information returned is the info of the group the
+ * server belongs to.
+ */
+public class ServerInformationReply extends RaftClientReply {
+  RaftGroup group;
+
+  public ServerInformationReply(RaftClientRequest request, Message message,
+      RaftGroup group) {
+    super(request, message);
+    this.group = group;
+  }
+
+  public ServerInformationReply(RaftClientRequest request,
+      RaftException ex) {
+    super(request, ex);
+  }
+
+  public RaftGroup getGroup() {
+    return group;
+  }
+
+  public ServerInformationReply(ClientId clientId, RaftPeerId serverId,
+      RaftGroupId groupId, long callId, boolean success, Message message,
+      RaftException exception, RaftGroup group) {
+    super(clientId, serverId, groupId, callId, success, message, exception);
+    this.group = group;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-common/src/main/java/org/apache/ratis/protocol/ServerInformatonRequest.java
----------------------------------------------------------------------
diff --git 
a/ratis-common/src/main/java/org/apache/ratis/protocol/ServerInformatonRequest.java
 
b/ratis-common/src/main/java/org/apache/ratis/protocol/ServerInformatonRequest.java
new file mode 100644
index 0000000..b4792b2
--- /dev/null
+++ 
b/ratis-common/src/main/java/org/apache/ratis/protocol/ServerInformatonRequest.java
@@ -0,0 +1,12 @@
+package org.apache.ratis.protocol;
+
+/**
+ * Client sends this request to a server to request for the information about
+ * the server itself.
+ */
+public class ServerInformatonRequest extends RaftClientRequest {
+  public ServerInformatonRequest(ClientId clientId, RaftPeerId serverId,
+      RaftGroupId groupId, long callId) {
+    super(clientId, serverId, groupId, callId, null);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-grpc/src/main/java/org/apache/ratis/grpc/RaftGrpcUtil.java
----------------------------------------------------------------------
diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/RaftGrpcUtil.java 
b/ratis-grpc/src/main/java/org/apache/ratis/grpc/RaftGrpcUtil.java
index fdc9ce8..bae3682 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/RaftGrpcUtil.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/RaftGrpcUtil.java
@@ -31,6 +31,7 @@ import org.apache.ratis.util.StringUtils;
 
 import java.io.IOException;
 import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
 
 public interface RaftGrpcUtil {
   Metadata.Key<String> EXCEPTION_TYPE_KEY =
@@ -73,15 +74,16 @@ public interface RaftGrpcUtil {
     return e;
   }
 
-  static void asyncCall(
-      StreamObserver<RaftClientReplyProto> responseObserver,
-      CheckedSupplier<CompletableFuture<RaftClientReply>, IOException> 
supplier) {
+  static <REPLY extends RaftClientReply, REPLY_PROTO> void asyncCall(
+      StreamObserver<REPLY_PROTO> responseObserver,
+      CheckedSupplier<CompletableFuture<REPLY>, IOException> supplier,
+      Function<REPLY, REPLY_PROTO> toProto) {
     try {
       supplier.get().whenCompleteAsync((reply, exception) -> {
         if (exception != null) {
           responseObserver.onError(RaftGrpcUtil.wrapException(exception));
         } else {
-          
responseObserver.onNext(ClientProtoUtils.toRaftClientReplyProto(reply));
+          responseObserver.onNext(toProto.apply(reply));
           responseObserver.onCompleted();
         }
       });

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/GrpcClientRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/GrpcClientRpc.java 
b/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/GrpcClientRpc.java
index b30640b..2a27a75 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/GrpcClientRpc.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/GrpcClientRpc.java
@@ -18,6 +18,7 @@
 package org.apache.ratis.grpc.client;
 
 import org.apache.ratis.client.RaftClientRpc;
+import org.apache.ratis.client.impl.ClientProtoUtils;
 import org.apache.ratis.grpc.RaftGrpcUtil;
 import org.apache.ratis.protocol.*;
 import org.apache.ratis.shaded.io.grpc.StatusRuntimeException;
@@ -28,6 +29,7 @@ import 
org.apache.ratis.shaded.proto.RaftProtos.RaftClientRequestProto;
 import org.apache.ratis.shaded.proto.RaftProtos.SetConfigurationRequestProto;
 import org.apache.ratis.util.IOUtils;
 import org.apache.ratis.util.PeerProxyMap;
+import org.apache.ratis.util.ProtoUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -57,6 +59,11 @@ public class GrpcClientRpc implements RaftClientRpc {
       SetConfigurationRequestProto setConf =
           toSetConfigurationRequestProto((SetConfigurationRequest) request);
       return toRaftClientReply(proxy.setConfiguration(setConf));
+    } else if (request instanceof ServerInformatonRequest){
+      RaftProtos.ServerInformationRequestProto proto =
+          toServerInformationRequestProto((ServerInformatonRequest) request);
+      return ClientProtoUtils.toServerInformationReply(
+          proxy.serverInformation(proto));
     } else {
       RaftClientRequestProto requestProto = toRaftClientRequestProto(request);
       CompletableFuture<RaftClientReplyProto> replyFuture =

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/RaftClientProtocolClient.java
----------------------------------------------------------------------
diff --git 
a/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/RaftClientProtocolClient.java
 
b/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/RaftClientProtocolClient.java
index 21254f3..12a3a5b 100644
--- 
a/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/RaftClientProtocolClient.java
+++ 
b/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/RaftClientProtocolClient.java
@@ -23,6 +23,8 @@ import org.apache.ratis.shaded.io.grpc.ManagedChannel;
 import org.apache.ratis.shaded.io.grpc.ManagedChannelBuilder;
 import org.apache.ratis.shaded.io.grpc.StatusRuntimeException;
 import org.apache.ratis.shaded.io.grpc.stub.StreamObserver;
+import org.apache.ratis.shaded.proto.RaftProtos.ServerInformationRequestProto;
+import org.apache.ratis.shaded.proto.RaftProtos.ServerInformationReplyProto;
 import org.apache.ratis.shaded.proto.RaftProtos.RaftClientReplyProto;
 import org.apache.ratis.shaded.proto.RaftProtos.RaftClientRequestProto;
 import org.apache.ratis.shaded.proto.RaftProtos.ReinitializeRequestProto;
@@ -63,6 +65,11 @@ public class RaftClientProtocolClient implements Closeable {
     return blockingCall(() -> adminBlockingStub.reinitialize(request));
   }
 
+  ServerInformationReplyProto serverInformation(
+      ServerInformationRequestProto request) throws IOException {
+    return adminBlockingStub.serverInformation(request);
+  }
+
   RaftClientReplyProto setConfiguration(
       SetConfigurationRequestProto request) throws IOException {
     return blockingCall(() -> blockingStub.setConfiguration(request));

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/RaftClientProtocolService.java
----------------------------------------------------------------------
diff --git 
a/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/RaftClientProtocolService.java
 
b/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/RaftClientProtocolService.java
index e11a9cf..1cd913a 100644
--- 
a/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/RaftClientProtocolService.java
+++ 
b/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/RaftClientProtocolService.java
@@ -78,7 +78,8 @@ public class RaftClientProtocolService extends 
RaftClientProtocolServiceImplBase
   public void setConfiguration(SetConfigurationRequestProto proto,
       StreamObserver<RaftClientReplyProto> responseObserver) {
     final SetConfigurationRequest request = 
ClientProtoUtils.toSetConfigurationRequest(proto);
-    RaftGrpcUtil.asyncCall(responseObserver, () -> 
protocol.setConfigurationAsync(request));
+    RaftGrpcUtil.asyncCall(responseObserver, () -> 
protocol.setConfigurationAsync(request),
+        ClientProtoUtils::toRaftClientReplyProto);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/AdminProtocolService.java
----------------------------------------------------------------------
diff --git 
a/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/AdminProtocolService.java
 
b/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/AdminProtocolService.java
index 4e7ff9a..d2aae53 100644
--- 
a/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/AdminProtocolService.java
+++ 
b/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/AdminProtocolService.java
@@ -22,9 +22,12 @@ import org.apache.ratis.grpc.RaftGrpcUtil;
 import org.apache.ratis.protocol.AdminAsynchronousProtocol;
 import org.apache.ratis.protocol.RaftPeerId;
 import org.apache.ratis.protocol.ReinitializeRequest;
+import org.apache.ratis.protocol.ServerInformatonRequest;
 import org.apache.ratis.shaded.io.grpc.stub.StreamObserver;
+import org.apache.ratis.shaded.proto.RaftProtos.ServerInformationReplyProto;
 import org.apache.ratis.shaded.proto.RaftProtos.RaftClientReplyProto;
 import org.apache.ratis.shaded.proto.RaftProtos.ReinitializeRequestProto;
+import org.apache.ratis.shaded.proto.RaftProtos.ServerInformationRequestProto;
 import 
org.apache.ratis.shaded.proto.grpc.AdminProtocolServiceGrpc.AdminProtocolServiceImplBase;
 
 public class AdminProtocolService extends AdminProtocolServiceImplBase {
@@ -40,6 +43,15 @@ public class AdminProtocolService extends 
AdminProtocolServiceImplBase {
   public void reinitialize(ReinitializeRequestProto proto,
                            StreamObserver<RaftClientReplyProto> 
responseObserver) {
     final ReinitializeRequest request = 
ClientProtoUtils.toReinitializeRequest(proto);
-    RaftGrpcUtil.asyncCall(responseObserver, () -> 
protocol.reinitializeAsync(request));
+    RaftGrpcUtil.asyncCall(responseObserver, () -> 
protocol.reinitializeAsync(request),
+        ClientProtoUtils::toRaftClientReplyProto);
+  }
+
+  @Override
+  public void serverInformation(ServerInformationRequestProto proto,
+      StreamObserver<ServerInformationReplyProto> responseObserver) {
+    final ServerInformatonRequest request = 
ClientProtoUtils.toServerInformationRequest(proto);
+    RaftGrpcUtil.asyncCall(responseObserver, () -> 
protocol.getInfoAsync(request),
+        ClientProtoUtils::toServerInformationReplyProto);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestServerInformationWithGrpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestServerInformationWithGrpc.java
 
b/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestServerInformationWithGrpc.java
new file mode 100644
index 0000000..ef978a1
--- /dev/null
+++ 
b/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestServerInformationWithGrpc.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.server.impl.ServerInformationBaseTest;
+
+public class TestServerInformationWithGrpc
+    extends ServerInformationBaseTest<MiniRaftClusterWithGRpc>
+    implements MiniRaftClusterWithGRpc.FactoryGet {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/CombinedClientProtocolClientSideTranslatorPB.java
----------------------------------------------------------------------
diff --git 
a/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/CombinedClientProtocolClientSideTranslatorPB.java
 
b/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/CombinedClientProtocolClientSideTranslatorPB.java
index 710b691..9e5e7a8 100644
--- 
a/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/CombinedClientProtocolClientSideTranslatorPB.java
+++ 
b/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/CombinedClientProtocolClientSideTranslatorPB.java
@@ -24,9 +24,10 @@ import org.apache.ratis.hadooprpc.Proxy;
 import org.apache.ratis.protocol.RaftClientReply;
 import org.apache.ratis.protocol.RaftClientRequest;
 import org.apache.ratis.protocol.ReinitializeRequest;
+import org.apache.ratis.protocol.ServerInformatonRequest;
+import org.apache.ratis.protocol.ServerInformationReply;
 import org.apache.ratis.protocol.SetConfigurationRequest;
 import org.apache.ratis.shaded.com.google.protobuf.ServiceException;
-import org.apache.ratis.shaded.proto.RaftProtos.RaftClientReplyProto;
 import org.apache.ratis.util.CheckedFunction;
 import org.apache.ratis.util.ProtoUtils;
 import org.slf4j.Logger;
@@ -50,31 +51,48 @@ public class CombinedClientProtocolClientSideTranslatorPB
   @Override
   public RaftClientReply submitClientRequest(RaftClientRequest request)
       throws IOException {
-    return handleRequest(request, ClientProtoUtils::toRaftClientRequestProto,
+    return handleRequest(request,
+        ClientProtoUtils::toRaftClientRequestProto,
+        ClientProtoUtils::toRaftClientReply,
         p -> getProtocol().submitClientRequest(null, p));
   }
 
   @Override
   public RaftClientReply setConfiguration(SetConfigurationRequest request)
       throws IOException {
-    return handleRequest(request, 
ClientProtoUtils::toSetConfigurationRequestProto,
+    return handleRequest(request,
+        ClientProtoUtils::toSetConfigurationRequestProto,
+        ClientProtoUtils::toRaftClientReply,
         p -> getProtocol().setConfiguration(null, p));
   }
 
   @Override
   public RaftClientReply reinitialize(ReinitializeRequest request) throws 
IOException {
-    return handleRequest(request, ClientProtoUtils::toReinitializeRequestProto,
-      p -> getProtocol().reinitialize(null, p));
+    return handleRequest(request,
+        ClientProtoUtils::toReinitializeRequestProto,
+        ClientProtoUtils::toRaftClientReply,
+        p -> getProtocol().reinitialize(null, p));
   }
 
-  static <REQUEST extends RaftClientRequest, PROTO> RaftClientReply 
handleRequest(
-      REQUEST request, Function<REQUEST, PROTO> toProto,
-      CheckedFunction<PROTO, RaftClientReplyProto, ServiceException> handler)
+  @Override
+  public ServerInformationReply getInfo(ServerInformatonRequest request) 
throws IOException {
+    return handleRequest(request,
+        ClientProtoUtils::toServerInformationRequestProto,
+        ClientProtoUtils::toServerInformationReply,
+        p -> getProtocol().serverInformation(null, p));
+  }
+
+  static <REQUEST extends RaftClientRequest, REPLY extends RaftClientReply,
+      PROTO_REQ, PROTO_REP> REPLY handleRequest(
+      REQUEST request,
+      Function<REQUEST, PROTO_REQ> reqToProto,
+      Function<PROTO_REP, REPLY> repToProto,
+      CheckedFunction<PROTO_REQ, PROTO_REP, ServiceException> handler)
       throws IOException {
-    final PROTO proto = toProto.apply(request);
+    final PROTO_REQ proto = reqToProto.apply(request);
     try {
-      final RaftClientReplyProto reply = handler.apply(proto);
-      return ClientProtoUtils.toRaftClientReply(reply);
+      final PROTO_REP reply = handler.apply(proto);
+      return repToProto.apply(reply);
     } catch (ServiceException se) {
       LOG.trace("Failed to handle " + request, se);
       throw ProtoUtils.toIOException(se);

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/CombinedClientProtocolServerSideTranslatorPB.java
----------------------------------------------------------------------
diff --git 
a/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/CombinedClientProtocolServerSideTranslatorPB.java
 
b/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/CombinedClientProtocolServerSideTranslatorPB.java
index ef9e733..7b370cb 100644
--- 
a/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/CombinedClientProtocolServerSideTranslatorPB.java
+++ 
b/ratis-hadoop/src/main/java/org/apache/ratis/hadooprpc/client/CombinedClientProtocolServerSideTranslatorPB.java
@@ -29,6 +29,8 @@ import 
org.apache.ratis.shaded.proto.RaftProtos.RaftClientReplyProto;
 import org.apache.ratis.shaded.proto.RaftProtos.RaftClientRequestProto;
 import org.apache.ratis.shaded.proto.RaftProtos.SetConfigurationRequestProto;
 import org.apache.ratis.shaded.proto.RaftProtos.ReinitializeRequestProto;
+import org.apache.ratis.shaded.proto.RaftProtos.ServerInformationRequestProto;
+import org.apache.ratis.shaded.proto.RaftProtos.ServerInformationReplyProto;
 
 @InterfaceAudience.Private
 public class CombinedClientProtocolServerSideTranslatorPB
@@ -79,4 +81,18 @@ public class CombinedClientProtocolServerSideTranslatorPB
       throw new ServiceException(ioe);
     }
   }
+
+  @Override
+  public ServerInformationReplyProto serverInformation(
+      RpcController controller, ServerInformationRequestProto proto)
+    throws ServiceException {
+    final ServerInformatonRequest request;
+    try {
+      request = ClientProtoUtils.toServerInformationRequest(proto);
+      final ServerInformationReply reply = impl.getInfo(request);
+      return ClientProtoUtils.toServerInformationReplyProto(reply);
+    } catch (IOException ioe) {
+      throw new ServiceException(ioe);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/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 c5a6ba9..b35d76b 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
@@ -45,6 +45,8 @@ public class HadoopClientRpc implements RaftClientRpc {
         return proxy.reinitialize((ReinitializeRequest) request);
       } else if (request instanceof SetConfigurationRequest) {
         return proxy.setConfiguration((SetConfigurationRequest) request);
+      } else if (request instanceof ServerInformatonRequest) {
+        return proxy.getInfo((ServerInformatonRequest) request);
       } else {
         return proxy.submitClientRequest(request);
       }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestServerInformationWithHadoopRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestServerInformationWithHadoopRpc.java
 
b/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestServerInformationWithHadoopRpc.java
new file mode 100644
index 0000000..4e54426
--- /dev/null
+++ 
b/ratis-hadoop/src/test/java/org/apache/ratis/hadooprpc/TestServerInformationWithHadoopRpc.java
@@ -0,0 +1,8 @@
+package org.apache.ratis.hadooprpc;
+
+import org.apache.ratis.server.impl.ServerInformationBaseTest;
+
+public class TestServerInformationWithHadoopRpc
+    extends ServerInformationBaseTest<MiniRaftClusterWithHadoopRpc>
+    implements MiniRaftClusterWithHadoopRpc.Factory.Get{
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientRpc.java 
b/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientRpc.java
index 14218ad..8a1af8a 100644
--- 
a/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientRpc.java
+++ 
b/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientRpc.java
@@ -21,6 +21,7 @@ import org.apache.ratis.client.RaftClientRpc;
 import org.apache.ratis.client.impl.ClientProtoUtils;
 import org.apache.ratis.netty.NettyRpcProxy;
 import org.apache.ratis.protocol.*;
+import org.apache.ratis.shaded.proto.RaftProtos;
 import org.apache.ratis.shaded.proto.RaftProtos.RaftClientRequestProto;
 import org.apache.ratis.shaded.proto.RaftProtos.RaftRpcRequestProto;
 import org.apache.ratis.shaded.proto.RaftProtos.ReinitializeRequestProto;
@@ -49,13 +50,23 @@ public class NettyClientRpc implements RaftClientRpc {
           (SetConfigurationRequest)request);
       b.setSetConfigurationRequest(proto);
       rpcRequest = proto.getRpcRequest();
+    } else if (request instanceof ServerInformatonRequest) {
+      final RaftProtos.ServerInformationRequestProto proto = 
ClientProtoUtils.toServerInformationRequestProto(
+          (ServerInformatonRequest)request);
+      b.setServerInformationRequest(proto);
+      rpcRequest = proto.getRpcRequest();
     } else {
       final RaftClientRequestProto proto = 
ClientProtoUtils.toRaftClientRequestProto(request);
       b.setRaftClientRequest(proto);
       rpcRequest = proto.getRpcRequest();
     }
-    return ClientProtoUtils.toRaftClientReply(
-        proxy.send(rpcRequest, b.build()).getRaftClientReply());
+    if (request instanceof ServerInformatonRequest) {
+      return ClientProtoUtils.toServerInformationReply(
+          proxy.send(rpcRequest, b.build()).getServerInfoReply());
+    } else {
+      return ClientProtoUtils.toRaftClientReply(
+          proxy.send(rpcRequest, b.build()).getRaftClientReply());
+    }
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-netty/src/main/java/org/apache/ratis/netty/server/NettyRpcService.java
----------------------------------------------------------------------
diff --git 
a/ratis-netty/src/main/java/org/apache/ratis/netty/server/NettyRpcService.java 
b/ratis-netty/src/main/java/org/apache/ratis/netty/server/NettyRpcService.java
index b8028b6..3461254 100644
--- 
a/ratis-netty/src/main/java/org/apache/ratis/netty/server/NettyRpcService.java
+++ 
b/ratis-netty/src/main/java/org/apache/ratis/netty/server/NettyRpcService.java
@@ -23,6 +23,7 @@ import org.apache.ratis.netty.NettyRpcProxy;
 import org.apache.ratis.protocol.RaftClientReply;
 import org.apache.ratis.protocol.RaftPeer;
 import org.apache.ratis.protocol.RaftPeerId;
+import org.apache.ratis.protocol.ServerInformationReply;
 import org.apache.ratis.rpc.SupportedRpcType;
 import org.apache.ratis.server.RaftServer;
 import org.apache.ratis.server.RaftServerRpc;
@@ -209,6 +210,15 @@ public final class NettyRpcService implements 
RaftServerRpc {
               
.setRaftClientReply(ClientProtoUtils.toRaftClientReplyProto(reply))
               .build();
         }
+        case SERVERINFORMATIONREQUEST: {
+          final ServerInformationRequestProto request = 
proto.getServerInformationRequest();
+          rpcRequest = request.getRpcRequest();
+          final ServerInformationReply reply = server.getInfo(
+              ClientProtoUtils.toServerInformationRequest(request));
+          return RaftNettyServerReplyProto.newBuilder()
+              
.setServerInfoReply(ClientProtoUtils.toServerInformationReplyProto(reply))
+              .build();
+        }
         case RAFTNETTYSERVERREQUEST_NOT_SET:
           throw new IllegalArgumentException("Request case not set in proto: "
               + proto.getRaftNettyServerRequestCase());

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-netty/src/test/java/org/apache/ratis/netty/TestServerInformationWithNetty.java
----------------------------------------------------------------------
diff --git 
a/ratis-netty/src/test/java/org/apache/ratis/netty/TestServerInformationWithNetty.java
 
b/ratis-netty/src/test/java/org/apache/ratis/netty/TestServerInformationWithNetty.java
new file mode 100644
index 0000000..f586af5
--- /dev/null
+++ 
b/ratis-netty/src/test/java/org/apache/ratis/netty/TestServerInformationWithNetty.java
@@ -0,0 +1,8 @@
+package org.apache.ratis.netty;
+
+import org.apache.ratis.server.impl.ServerInformationBaseTest;
+
+public class TestServerInformationWithNetty
+    extends ServerInformationBaseTest<MiniRaftClusterWithNetty>
+    implements MiniRaftClusterWithNetty.FactoryGet {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-proto-shaded/src/main/proto/GRpc.proto
----------------------------------------------------------------------
diff --git a/ratis-proto-shaded/src/main/proto/GRpc.proto 
b/ratis-proto-shaded/src/main/proto/GRpc.proto
index 599227b..375079f 100644
--- a/ratis-proto-shaded/src/main/proto/GRpc.proto
+++ b/ratis-proto-shaded/src/main/proto/GRpc.proto
@@ -48,4 +48,7 @@ service AdminProtocolService {
   // A client-to-server RPC to reinitialize the server
   rpc reinitialize(ratis.common.ReinitializeRequestProto)
       returns(ratis.common.RaftClientReplyProto) {}
+
+  rpc serverInformation(ratis.common.ServerInformationRequestProto)
+      returns(ratis.common.ServerInformationReplyProto) {}
 }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-proto-shaded/src/main/proto/Hadoop.proto
----------------------------------------------------------------------
diff --git a/ratis-proto-shaded/src/main/proto/Hadoop.proto 
b/ratis-proto-shaded/src/main/proto/Hadoop.proto
index 48cfbf4..872c455 100644
--- a/ratis-proto-shaded/src/main/proto/Hadoop.proto
+++ b/ratis-proto-shaded/src/main/proto/Hadoop.proto
@@ -33,6 +33,9 @@ service CombinedClientProtocolService {
 
   rpc reinitialize(ratis.common.ReinitializeRequestProto)
       returns(ratis.common.RaftClientReplyProto);
+
+  rpc serverInformation(ratis.common.ServerInformationRequestProto)
+      returns(ratis.common.ServerInformationReplyProto);
 }
 
 service RaftServerProtocolService {

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-proto-shaded/src/main/proto/Netty.proto
----------------------------------------------------------------------
diff --git a/ratis-proto-shaded/src/main/proto/Netty.proto 
b/ratis-proto-shaded/src/main/proto/Netty.proto
index 4de770f..c034dd5 100644
--- a/ratis-proto-shaded/src/main/proto/Netty.proto
+++ b/ratis-proto-shaded/src/main/proto/Netty.proto
@@ -36,6 +36,7 @@ message RaftNettyServerRequestProto {
     ratis.common.RaftClientRequestProto raftClientRequest = 4;
     ratis.common.SetConfigurationRequestProto setConfigurationRequest = 5;
     ratis.common.ReinitializeRequestProto reinitializeRequest = 6;
+    ratis.common.ServerInformationRequestProto serverInformationRequest = 7;
   }
 }
 
@@ -45,6 +46,7 @@ message RaftNettyServerReplyProto {
     ratis.common.AppendEntriesReplyProto appendEntriesReply = 2;
     ratis.common.InstallSnapshotReplyProto installSnapshotReply = 3;
     ratis.common.RaftClientReplyProto raftClientReply = 4;
-    RaftNettyExceptionReplyProto exceptionReply = 5;
+    ratis.common.ServerInformationReplyProto serverInfoReply = 5;
+    RaftNettyExceptionReplyProto exceptionReply = 6;
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-proto-shaded/src/main/proto/Raft.proto
----------------------------------------------------------------------
diff --git a/ratis-proto-shaded/src/main/proto/Raft.proto 
b/ratis-proto-shaded/src/main/proto/Raft.proto
index 4dada3b..52a1761 100644
--- a/ratis-proto-shaded/src/main/proto/Raft.proto
+++ b/ratis-proto-shaded/src/main/proto/Raft.proto
@@ -186,6 +186,11 @@ message RaftClientReplyProto {
   }
 }
 
+message ServerInformationReplyProto {
+  RaftRpcReplyProto rpcReply = 1;
+  RaftGroupProto group = 2;
+}
+
 // setConfiguration request
 message SetConfigurationRequestProto {
   RaftRpcRequestProto rpcRequest = 1;
@@ -197,3 +202,8 @@ message ReinitializeRequestProto {
   RaftRpcRequestProto rpcRequest = 1;
   RaftGroupProto group = 2; // the target group.
 }
+
+// server info request
+message ServerInformationRequestProto {
+  RaftRpcRequestProto rpcRequest = 1;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/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 2d38fe7..13ef7e8 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
@@ -44,6 +44,7 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
+import java.util.function.BiFunction;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
@@ -486,6 +487,13 @@ public class RaftServerImpl implements RaftServerProtocol,
   static RaftClientReply waitForReply(RaftPeerId id,
       RaftClientRequest request, CompletableFuture<RaftClientReply> future)
       throws IOException {
+    return waitForReply(id, request, future, RaftClientReply::new);
+  }
+
+  static <REPLY extends RaftClientReply> REPLY waitForReply(
+      RaftPeerId id, RaftClientRequest request, CompletableFuture<REPLY> 
future,
+      BiFunction<RaftClientRequest, RaftException, REPLY> exceptionReply)
+      throws IOException {
     try {
       return future.get();
     } catch (InterruptedException e) {
@@ -499,7 +507,7 @@ public class RaftServerImpl implements RaftServerProtocol,
       }
       if (cause instanceof NotLeaderException ||
           cause instanceof StateMachineException) {
-        return new RaftClientReply(request, (RaftException) cause);
+        return exceptionReply.apply(request, (RaftException) cause);
       } else {
         throw IOUtils.asIOException(cause);
       }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/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 d2bf54c..0595027 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
@@ -32,6 +32,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.atomic.AtomicReference;
@@ -190,6 +192,31 @@ public class RaftServerProxy implements RaftServer {
     });
   }
 
+  @Override
+  public ServerInformationReply getInfo(ServerInformatonRequest request)
+      throws IOException {
+    return RaftServerImpl.waitForReply(getId(), request, getInfoAsync(request),
+        ServerInformationReply::new);
+  }
+
+  @Override
+  public CompletableFuture<ServerInformationReply> getInfoAsync(
+      ServerInformatonRequest request) throws IOException {
+    return CompletableFuture.supplyAsync(() -> {
+      try {
+        RaftServerImpl server = impl.get();
+        Collection<RaftPeer> peers = server.getRaftConf().getPeers();
+        RaftGroupId groupId = server.getGroupId();
+        RaftGroup group = new RaftGroup(groupId, peers);
+        return new ServerInformationReply(request, null, group);
+      } catch (Exception e) {
+        final RaftException re = new RaftException(
+            "Failed to get info, request=" + request, e);
+        return new ServerInformationReply(request, re);
+      }
+    });
+  }
+
   /**
    * Handle a raft configuration change request from client.
    */

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-server/src/test/java/org/apache/ratis/server/impl/ServerInformationBaseTest.java
----------------------------------------------------------------------
diff --git 
a/ratis-server/src/test/java/org/apache/ratis/server/impl/ServerInformationBaseTest.java
 
b/ratis-server/src/test/java/org/apache/ratis/server/impl/ServerInformationBaseTest.java
new file mode 100644
index 0000000..c9fe997
--- /dev/null
+++ 
b/ratis-server/src/test/java/org/apache/ratis/server/impl/ServerInformationBaseTest.java
@@ -0,0 +1,103 @@
+/**
+ * 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.impl;
+
+import org.apache.log4j.Level;
+import org.apache.ratis.BaseTest;
+import org.apache.ratis.MiniRaftCluster;
+import org.apache.ratis.client.RaftClient;
+import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.protocol.RaftClientReply;
+import org.apache.ratis.protocol.RaftGroup;
+import org.apache.ratis.protocol.RaftPeer;
+import org.apache.ratis.protocol.ServerInformationReply;
+import org.apache.ratis.util.LogUtils;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+import static org.apache.ratis.util.Preconditions.assertTrue;
+
+public abstract class ServerInformationBaseTest<CLUSTER extends 
MiniRaftCluster>
+    extends BaseTest
+    implements MiniRaftCluster.Factory.Get<CLUSTER> {
+  static {
+    LogUtils.setLogLevel(RaftServerImpl.LOG, Level.DEBUG);
+    LogUtils.setLogLevel(RaftClient.LOG, Level.DEBUG);
+  }
+
+  static final RaftProperties prop = new RaftProperties();
+
+  public MiniRaftCluster getCluster(int peerNum) throws IOException {
+    return getFactory().newCluster(peerNum, prop);
+  }
+
+  @Test
+  public void testServerInformation() throws Exception {
+    runTest(5);
+  }
+
+
+  private void runTest(int num) throws Exception {
+    LOG.info("Running server info test with " + num);
+
+    final MiniRaftCluster cluster = getCluster(num);
+
+    cluster.start();
+    // all the peers in the cluster are in the same group, get it.
+    RaftGroup group = cluster.getGroup();
+
+    List<RaftPeer> peers = cluster.getPeers();
+    // check that all the peers return exactly this group when group 
information
+    // is requested.
+    for (RaftPeer peer : peers) {
+      try(final RaftClient client = cluster.createClient(peer.getId())) {
+        RaftClientReply reply = client.serverInformation(peer.getId());
+        assertTrue(reply instanceof ServerInformationReply);
+        ServerInformationReply info = (ServerInformationReply)reply;
+        assertTrue(sameGroup(group, info.getGroup()));
+      }
+    }
+  }
+
+  private boolean sameGroup(RaftGroup expected, RaftGroup given) {
+    if (!expected.getGroupId().toString().equals(
+        given.getGroupId().toString())) {
+      return false;
+    }
+    List<RaftPeer> expectedPeers = expected.getPeers();
+    List<RaftPeer> givenPeers = given.getPeers();
+    if (expectedPeers.size() != givenPeers.size()) {
+      return false;
+    }
+    for (RaftPeer peerGiven : givenPeers) {
+      boolean found = false;
+      for (RaftPeer peerExpect : expectedPeers) {
+        if (peerGiven.getId().toString().equals(
+            peerExpect.getId().toString())) {
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        return false;
+      }
+    }
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-server/src/test/java/org/apache/ratis/server/simulation/SimulatedServerRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-server/src/test/java/org/apache/ratis/server/simulation/SimulatedServerRpc.java
 
b/ratis-server/src/test/java/org/apache/ratis/server/simulation/SimulatedServerRpc.java
index 44313dd..91b7ad5 100644
--- 
a/ratis-server/src/test/java/org/apache/ratis/server/simulation/SimulatedServerRpc.java
+++ 
b/ratis-server/src/test/java/org/apache/ratis/server/simulation/SimulatedServerRpc.java
@@ -152,6 +152,9 @@ class SimulatedServerRpc implements RaftServerRpc {
       if (request instanceof ReinitializeRequest) {
         future = CompletableFuture.completedFuture(
             server.reinitialize((ReinitializeRequest) request));
+      } else if (request instanceof ServerInformatonRequest) {
+        future = CompletableFuture.completedFuture(
+            server.getInfo((ServerInformatonRequest) request));
       } else if (request instanceof SetConfigurationRequest) {
         future = server.setConfigurationAsync((SetConfigurationRequest) 
request);
       } else {

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/298c1a2d/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestServerInformationWithSimulatedRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestServerInformationWithSimulatedRpc.java
 
b/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestServerInformationWithSimulatedRpc.java
new file mode 100644
index 0000000..f7025a5
--- /dev/null
+++ 
b/ratis-server/src/test/java/org/apache/ratis/server/simulation/TestServerInformationWithSimulatedRpc.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.server.impl.ServerInformationBaseTest;
+
+public class TestServerInformationWithSimulatedRpc
+    extends ServerInformationBaseTest<MiniRaftClusterWithSimulatedRpc>
+    implements MiniRaftClusterWithSimulatedRpc.FactoryGet {
+}

Reply via email to