Move inner classes out of Server.

This is only a move with appropriate code changes to use the new class
names. The only functional change was changing the visibility of
MethodDefinition's constructor to package-private so ServiceDefinition
could construct it.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=73569974
This commit is contained in:
ejona 2014-08-18 14:30:24 -07:00 committed by Eric Anderson
parent ba71ee91bf
commit 34aede347a
12 changed files with 404 additions and 389 deletions

View File

@ -1,7 +1,7 @@
package com.google.net.stubby;
import com.google.net.stubby.Server.MethodDefinition;
import com.google.net.stubby.Server.ServiceDefinition;
import com.google.net.stubby.ServerMethodDefinition;
import com.google.net.stubby.ServerServiceDefinition;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
@ -15,19 +15,19 @@ public abstract class HandlerRegistry {
/** A method definition and its parent's service definition. */
public static final class Method {
private final ServiceDefinition serviceDef;
private final MethodDefinition methodDef;
private final ServerServiceDefinition serviceDef;
private final ServerMethodDefinition methodDef;
public Method(ServiceDefinition serviceDef, MethodDefinition methodDef) {
public Method(ServerServiceDefinition serviceDef, ServerMethodDefinition methodDef) {
this.serviceDef = serviceDef;
this.methodDef = methodDef;
}
public ServiceDefinition getServiceDefinition() {
public ServerServiceDefinition getServiceDefinition() {
return serviceDef;
}
public MethodDefinition getMethodDefinition() {
public ServerMethodDefinition getMethodDefinition() {
return methodDef;
}
}

View File

@ -1,6 +1,6 @@
package com.google.net.stubby;
import com.google.net.stubby.Server.ServiceDefinition;
import com.google.net.stubby.ServerServiceDefinition;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
@ -12,9 +12,9 @@ public abstract class MutableHandlerRegistry extends HandlerRegistry {
* Returns {@code null}, or previous service if {@code service} replaced an existing service.
*/
@Nullable
public abstract ServiceDefinition addService(ServiceDefinition service);
public abstract ServerServiceDefinition addService(ServerServiceDefinition service);
/** Returns {@code false} if {@code service} was not registered. */
@Nullable
public abstract boolean removeService(ServiceDefinition service);
public abstract boolean removeService(ServerServiceDefinition service);
}

View File

@ -1,7 +1,7 @@
package com.google.net.stubby;
import com.google.net.stubby.Server.MethodDefinition;
import com.google.net.stubby.Server.ServiceDefinition;
import com.google.net.stubby.ServerMethodDefinition;
import com.google.net.stubby.ServerServiceDefinition;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@ -13,17 +13,17 @@ import javax.annotation.concurrent.ThreadSafe;
/** Mutable registry implementation of services and their methods for dispatching incoming calls. */
@ThreadSafe
public final class MutableHandlerRegistryImpl extends MutableHandlerRegistry {
private final ConcurrentMap<String, ServiceDefinition> services
= new ConcurrentHashMap<String, ServiceDefinition>();
private final ConcurrentMap<String, ServerServiceDefinition> services
= new ConcurrentHashMap<String, ServerServiceDefinition>();
@Override
@Nullable
public ServiceDefinition addService(ServiceDefinition service) {
public ServerServiceDefinition addService(ServerServiceDefinition service) {
return services.put(service.getName(), service);
}
@Override
public boolean removeService(ServiceDefinition service) {
public boolean removeService(ServerServiceDefinition service) {
return services.remove(service.getName(), service);
}
@ -39,11 +39,11 @@ public final class MutableHandlerRegistryImpl extends MutableHandlerRegistry {
if (index == -1) {
return null;
}
ServiceDefinition service = services.get(methodName.substring(0, index));
ServerServiceDefinition service = services.get(methodName.substring(0, index));
if (service == null) {
return null;
}
MethodDefinition method = service.getMethod(methodName.substring(index + 1));
ServerMethodDefinition method = service.getMethod(methodName.substring(index + 1));
if (method == null) {
return null;
}

View File

@ -1,16 +1,7 @@
package com.google.net.stubby;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Service;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
/**
@ -18,268 +9,4 @@ import javax.annotation.concurrent.ThreadSafe;
* not expected to be implemented by application code or interceptors.
*/
@ThreadSafe
public interface Server extends Service {
/** Definition of a service to be exposed via a Server. */
final class ServiceDefinition {
public static ServiceDefinition.Builder builder(String serviceName) {
return new ServiceDefinition.Builder(serviceName);
}
private final String name;
private final ImmutableList<MethodDefinition> methods;
private final ImmutableMap<String, MethodDefinition> methodLookup;
private ServiceDefinition(String name, ImmutableList<MethodDefinition> methods,
Map<String, MethodDefinition> methodLookup) {
this.name = name;
this.methods = methods;
this.methodLookup = ImmutableMap.copyOf(methodLookup);
}
/** Simple name of the service. It is not an absolute path. */
public String getName() {
return name;
}
public ImmutableList<MethodDefinition> getMethods() {
return methods;
}
public MethodDefinition getMethod(String name) {
return methodLookup.get(name);
}
/** Builder for constructing Service instances. */
public static final class Builder {
private final String serviceName;
private final ImmutableList.Builder<MethodDefinition> methods = ImmutableList.builder();
private final Map<String, MethodDefinition> methodLookup
= new HashMap<String, MethodDefinition>();
private Builder(String serviceName) {
this.serviceName = serviceName;
}
/**
* Add a method to be supported by the service.
*
* @param name simple name of the method, without the service prefix
* @param requestMarshaller marshaller for deserializing incoming requests
* @param responseMarshaller marshaller for serializing outgoing responses
* @param handler handler for incoming calls
*/
public <ReqT, RespT> Builder addMethod(String name, Marshaller<ReqT> requestMarshaller,
Marshaller<RespT> responseMarshaller, CallHandler<ReqT, RespT> handler) {
Preconditions.checkNotNull(name, "name must not be null");
if (methodLookup.containsKey(name)) {
throw new IllegalStateException("Method by same name already registered");
}
MethodDefinition def = new MethodDefinition<ReqT, RespT>(name,
Preconditions.checkNotNull(requestMarshaller, "requestMarshaller must not be null"),
Preconditions.checkNotNull(responseMarshaller, "responseMarshaller must not be null"),
Preconditions.checkNotNull(handler, "handler must not be null"));
methodLookup.put(name, def);
methods.add(def);
return this;
}
/** Construct new ServiceDefinition. */
public ServiceDefinition build() {
return new ServiceDefinition(serviceName, methods.build(), methodLookup);
}
}
}
/** Definition of a method supported by a service. */
final class MethodDefinition<RequestT, ResponseT> {
private final String name;
private final Marshaller<RequestT> requestMarshaller;
private final Marshaller<ResponseT> responseMarshaller;
private final CallHandler<RequestT, ResponseT> handler;
// MethodDefinition has no form of public construction. It is only created within the context of
// a ServiceDefinition.Builder.
private MethodDefinition(String name, Marshaller<RequestT> requestMarshaller,
Marshaller<ResponseT> responseMarshaller, CallHandler<RequestT, ResponseT> handler) {
this.name = name;
this.requestMarshaller = requestMarshaller;
this.responseMarshaller = responseMarshaller;
this.handler = handler;
}
/** The simple name of the method. It is not an absolute path. */
public String getName() {
return name;
}
/** Marshaller for deserializing incoming requests. */
public Marshaller<RequestT> getRequestMarshaller() {
return requestMarshaller;
}
/** Marshaller for serializing outgoing responses. */
public Marshaller<ResponseT> getResponseMarshaller() {
return responseMarshaller;
}
/** Handler for incoming calls. */
public CallHandler<RequestT, ResponseT> getCallHandler() {
return handler;
}
}
/**
* Interface for intercepting incoming RPCs before the handler receives them.
*/
@ThreadSafe
interface Interceptor {
/**
* Intercept a new call. General semantics of {@link Server.CallHandler#startCall} apply. {@code
* next} may only be called once. Returned listener must not be {@code null}.
*
* <p>If the implementation throws an exception, {@code call} will be closed with an error.
* Implementations must not throw an exception if they started processing that may use {@code
* call} on another thread.
*
* @param method metadata concerning the call
* @param call object for responding
* @param next next processor in the interceptor chain
* @return listener for processing incoming messages for {@code call}
*/
<ReqT, RespT> Server.Call.Listener<ReqT> interceptCall(MethodDescriptor<ReqT, RespT> method,
Server.Call<ReqT, RespT> call, CallHandler<ReqT, RespT> next);
}
/**
* Interface to begin processing incoming RPCs. Advanced applications and generated code implement
* this interface to implement service methods.
*/
@ThreadSafe
interface CallHandler<ReqT, RespT> {
/**
* Produce a non-{@code null} listener for the incoming call. Implementations are free to call
* methods on {@code call} before this method has returned.
*
* <p>If the implementation throws an exception, {@code call} will be closed with an error.
* Implementations must not throw an exception if they started processing that may use {@code
* call} on another thread.
*
* @param method metadata concerning the call
* @param call object for responding
* @return listener for processing incoming messages for {@code call}
*/
Server.Call.Listener<ReqT> startCall(MethodDescriptor<ReqT, RespT> method,
Server.Call<ReqT, RespT> call);
}
/**
* Low-level method for communicating with a remote client during a single RPC. Unlike normal
* RPCs, calls may stream any number of requests and responses, although a single request and
* single response is most common. This API is generally intended for use generated handlers, but
* advanced applications may have need for it.
*
* <p>Any contexts must be sent before any payloads, which must be sent before closing.
*
* <p>No generic method for determining message receipt or providing acknowlegement is provided.
* Applications are expected to utilize normal payload messages for such signals, as a response
* natually acknowledges its request.
*
* <p>Methods are guaranteed to be non-blocking. Implementations are not required to be
* thread-safe.
*/
abstract class Call<RequestT, ResponseT> {
/**
* Callbacks for consuming incoming RPC messages.
*
* <p>Any contexts are guaranteed to arrive before any payloads, which are guaranteed before
* half close, which is guaranteed before completion.
*
* <p>Implementations are free to block for extended periods of time. Implementations are not
* required to be thread-safe.
*/
// TODO(user): We need to decide what to do in the case of server closing with non-cancellation
// before client half closes. It may be that we treat such a case as an error. If we permit such
// a case then we either get to generate a half close or purposefully omit it.
public abstract static class Listener<T> {
/**
* A request context has been received. Any context messages will precede payload messages.
*
* <p>The {@code value} {@link InputStream} will be closed when the returned future completes.
* If no future is returned, the value will be closed immediately after returning from this
* method.
*/
@Nullable
public abstract ListenableFuture<Void> onContext(String name, InputStream value);
/**
* A request payload has been receiveed. For streaming calls, there may be zero payload
* messages.
*/
@Nullable
public abstract ListenableFuture<Void> onPayload(T payload);
/**
* The client completed all message sending. However, the call may still be cancelled.
*/
public abstract void onHalfClose();
/**
* The call was cancelled and the server is encouraged to abort processing to save resources,
* since the client will not process any further messages. Cancellations can be caused by
* timeouts, explicit cancel by client, network errors, and similar.
*
* <p>There will be no further callbacks for the call.
*/
public abstract void onCancel();
/**
* The call is considered complete and {@link #onCancel} is guaranteed not to be called.
* However, the client is not guaranteed to have received all messages.
*
* <p>There will be no further callbacks for the call.
*/
public abstract void onCompleted();
}
/**
* Close the call with the provided status. No further sending or receiving will occur. If
* {@code status} is not equal to {@link Status#OK}, then the call is said to have failed.
*
* <p>If {@code status} is not {@link Status#CANCELLED} and no errors or cancellations are known
* to have occured, then a {@link Listener#onCompleted} notification should be expected.
* Otherwise {@link Listener#onCancel} has been or will be called.
*
* @throws IllegalStateException if call is already {@code close}d
*/
public abstract void close(Status status);
/**
* Send a context message. Context messages are intended for side-channel information like
* statistics and authentication.
*
* @param name key identifier of context
* @param value context value bytes
* @throws IllegalStateException if call is {@link #close}d, or after {@link #sendPayload}
*/
public abstract void sendContext(String name, InputStream value);
/**
* Send a payload message. Payload messages are the primary form of communication associated
* with RPCs. Multiple payload messages may exist for streaming calls.
*
* @param payload message
* @throws IllegalStateException if call is {@link #close}d
*/
public abstract void sendPayload(ResponseT payload);
/**
* Returns {@code true} when the call is cancelled and the server is encouraged to abort
* processing to save resources, since the client will not be processing any further methods.
* Cancellations can be caused by timeouts, explicit cancel by client, network errors, and
* similar.
*
* <p>This method may safely be called concurrently from multiple threads.
*/
public abstract boolean isCancelled();
}
}
public interface Server extends Service {}

View File

@ -0,0 +1,116 @@
package com.google.net.stubby;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.InputStream;
import javax.annotation.Nullable;
/**
* Low-level method for communicating with a remote client during a single RPC. Unlike normal RPCs,
* calls may stream any number of requests and responses, although a single request and single
* response is most common. This API is generally intended for use generated handlers, but advanced
* applications may have need for it.
*
* <p>Any contexts must be sent before any payloads, which must be sent before closing.
*
* <p>No generic method for determining message receipt or providing acknowlegement is provided.
* Applications are expected to utilize normal payload messages for such signals, as a response
* natually acknowledges its request.
*
* <p>Methods are guaranteed to be non-blocking. Implementations are not required to be thread-safe.
*/
public abstract class ServerCall<RequestT, ResponseT> {
/**
* Callbacks for consuming incoming RPC messages.
*
* <p>Any contexts are guaranteed to arrive before any payloads, which are guaranteed before half
* close, which is guaranteed before completion.
*
* <p>Implementations are free to block for extended periods of time. Implementations are not
* required to be thread-safe.
*/
// TODO(user): We need to decide what to do in the case of server closing with non-cancellation
// before client half closes. It may be that we treat such a case as an error. If we permit such
// a case then we either get to generate a half close or purposefully omit it.
public abstract static class Listener<T> {
/**
* A request context has been received. Any context messages will precede payload messages.
*
* <p>The {@code value} {@link InputStream} will be closed when the returned future completes.
* If no future is returned, the value will be closed immediately after returning from this
* method.
*/
@Nullable
public abstract ListenableFuture<Void> onContext(String name, InputStream value);
/**
* A request payload has been receiveed. For streaming calls, there may be zero payload
* messages.
*/
@Nullable
public abstract ListenableFuture<Void> onPayload(T payload);
/**
* The client completed all message sending. However, the call may still be cancelled.
*/
public abstract void onHalfClose();
/**
* The call was cancelled and the server is encouraged to abort processing to save resources,
* since the client will not process any further messages. Cancellations can be caused by
* timeouts, explicit cancel by client, network errors, and similar.
*
* <p>There will be no further callbacks for the call.
*/
public abstract void onCancel();
/**
* The call is considered complete and {@link #onCancel} is guaranteed not to be called.
* However, the client is not guaranteed to have received all messages.
*
* <p>There will be no further callbacks for the call.
*/
public abstract void onCompleted();
}
/**
* Close the call with the provided status. No further sending or receiving will occur. If {@code
* status} is not equal to {@link Status#OK}, then the call is said to have failed.
*
* <p>If {@code status} is not {@link Status#CANCELLED} and no errors or cancellations are known
* to have occured, then a {@link Listener#onCompleted} notification should be expected.
* Otherwise {@link Listener#onCancel} has been or will be called.
*
* @throws IllegalStateException if call is already {@code close}d
*/
public abstract void close(Status status);
/**
* Send a context message. Context messages are intended for side-channel information like
* statistics and authentication.
*
* @param name key identifier of context
* @param value context value bytes
* @throws IllegalStateException if call is {@link #close}d, or after {@link #sendPayload}
*/
public abstract void sendContext(String name, InputStream value);
/**
* Send a payload message. Payload messages are the primary form of communication associated with
* RPCs. Multiple payload messages may exist for streaming calls.
*
* @param payload message
* @throws IllegalStateException if call is {@link #close}d
*/
public abstract void sendPayload(ResponseT payload);
/**
* Returns {@code true} when the call is cancelled and the server is encouraged to abort
* processing to save resources, since the client will not be processing any further methods.
* Cancellations can be caused by timeouts, explicit cancel by client, network errors, and
* similar.
*
* <p>This method may safely be called concurrently from multiple threads.
*/
public abstract boolean isCancelled();
}

View File

@ -0,0 +1,25 @@
package com.google.net.stubby;
import javax.annotation.concurrent.ThreadSafe;
/**
* Interface to begin processing incoming RPCs. Advanced applications and generated code implement
* this interface to implement service methods.
*/
@ThreadSafe
public interface ServerCallHandler<ReqT, RespT> {
/**
* Produce a non-{@code null} listener for the incoming call. Implementations are free to call
* methods on {@code call} before this method has returned.
*
* <p>If the implementation throws an exception, {@code call} will be closed with an error.
* Implementations must not throw an exception if they started processing that may use {@code
* call} on another thread.
*
* @param method metadata concerning the call
* @param call object for responding
* @return listener for processing incoming messages for {@code call}
*/
ServerCall.Listener<ReqT> startCall(MethodDescriptor<ReqT, RespT> method,
ServerCall<ReqT, RespT> call);
}

View File

@ -0,0 +1,25 @@
package com.google.net.stubby;
import javax.annotation.concurrent.ThreadSafe;
/**
* Interface for intercepting incoming RPCs before the handler receives them.
*/
@ThreadSafe
public interface ServerInterceptor {
/**
* Intercept a new call. General semantics of {@link ServerCallHandler#startCall} apply. {@code
* next} may only be called once. Returned listener must not be {@code null}.
*
* <p>If the implementation throws an exception, {@code call} will be closed with an error.
* Implementations must not throw an exception if they started processing that may use {@code
* call} on another thread.
*
* @param method metadata concerning the call
* @param call object for responding
* @param next next processor in the interceptor chain
* @return listener for processing incoming messages for {@code call}
*/
<ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(MethodDescriptor<ReqT, RespT> method,
ServerCall<ReqT, RespT> call, ServerCallHandler<ReqT, RespT> next);
}

View File

@ -2,87 +2,89 @@ package com.google.net.stubby;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.net.stubby.Server.CallHandler;
import com.google.net.stubby.Server.Interceptor;
import com.google.net.stubby.Server.MethodDefinition;
import com.google.net.stubby.Server.ServiceDefinition;
import com.google.net.stubby.ServerMethodDefinition;
import com.google.net.stubby.ServerCallHandler;
import com.google.net.stubby.ServerInterceptor;
import com.google.net.stubby.ServerServiceDefinition;
import java.util.List;
import java.util.Iterator;
/** Utility class for {@link Server.Interceptor}s. */
/** Utility class for {@link ServerInterceptor}s. */
public class ServerInterceptors {
// Prevent instantiation
private ServerInterceptors() {}
/**
* Create a new {@code ServiceDefinition} whose {@link Server.CallHandler}s will call {@code
* interceptors} before calling the pre-existing {@code CallHandler}.
* Create a new {@code ServerServiceDefinition} whose {@link ServerCallHandler}s will call {@code
* interceptors} before calling the pre-existing {@code ServerCallHandler}.
*/
public static ServiceDefinition intercept(ServiceDefinition serviceDef,
List<Interceptor> interceptors) {
public static ServerServiceDefinition intercept(ServerServiceDefinition serviceDef,
List<ServerInterceptor> interceptors) {
Preconditions.checkNotNull(serviceDef);
List<Interceptor> immutableInterceptors = ImmutableList.copyOf(interceptors);
List<ServerInterceptor> immutableInterceptors = ImmutableList.copyOf(interceptors);
if (immutableInterceptors.isEmpty()) {
return serviceDef;
}
ServiceDefinition.Builder serviceDefBuilder = ServiceDefinition.builder(serviceDef.getName());
for (MethodDefinition<?, ?> method : serviceDef.getMethods()) {
ServerServiceDefinition.Builder serviceDefBuilder
= ServerServiceDefinition.builder(serviceDef.getName());
for (ServerMethodDefinition<?, ?> method : serviceDef.getMethods()) {
wrapAndAddMethod(serviceDefBuilder, method, immutableInterceptors);
}
return serviceDefBuilder.build();
}
private static <ReqT, RespT> void wrapAndAddMethod(ServiceDefinition.Builder serviceDefBuilder,
MethodDefinition<ReqT, RespT> method, List<Interceptor> interceptors) {
CallHandler<ReqT, RespT> callHandler
= InterceptCallHandler.create(interceptors, method.getCallHandler());
private static <ReqT, RespT> void wrapAndAddMethod(
ServerServiceDefinition.Builder serviceDefBuilder, ServerMethodDefinition<ReqT, RespT> method,
List<ServerInterceptor> interceptors) {
ServerCallHandler<ReqT, RespT> callHandler
= InterceptCallHandler.create(interceptors, method.getServerCallHandler());
serviceDefBuilder.addMethod(method.getName(), method.getRequestMarshaller(),
method.getResponseMarshaller(), callHandler);
}
private static class InterceptCallHandler<ReqT, RespT> implements CallHandler<ReqT, RespT> {
private static class InterceptCallHandler<ReqT, RespT> implements ServerCallHandler<ReqT, RespT> {
public static <ReqT, RespT> InterceptCallHandler<ReqT, RespT> create(
List<Interceptor> interceptors, CallHandler<ReqT, RespT> callHandler) {
List<ServerInterceptor> interceptors, ServerCallHandler<ReqT, RespT> callHandler) {
return new InterceptCallHandler<ReqT, RespT>(interceptors, callHandler);
}
private final List<Interceptor> interceptors;
private final CallHandler<ReqT, RespT> callHandler;
private final List<ServerInterceptor> interceptors;
private final ServerCallHandler<ReqT, RespT> callHandler;
private InterceptCallHandler(List<Interceptor> interceptors,
CallHandler<ReqT, RespT> callHandler) {
private InterceptCallHandler(List<ServerInterceptor> interceptors,
ServerCallHandler<ReqT, RespT> callHandler) {
this.interceptors = interceptors;
this.callHandler = callHandler;
}
@Override
public Server.Call.Listener<ReqT> startCall(MethodDescriptor<ReqT, RespT> method,
Server.Call<ReqT, RespT> call) {
public ServerCall.Listener<ReqT> startCall(MethodDescriptor<ReqT, RespT> method,
ServerCall<ReqT, RespT> call) {
return ProcessInterceptorsCallHandler.create(interceptors.iterator(), callHandler)
.startCall(method, call);
}
}
private static class ProcessInterceptorsCallHandler<ReqT, RespT>
implements CallHandler<ReqT, RespT> {
implements ServerCallHandler<ReqT, RespT> {
public static <ReqT, RespT> ProcessInterceptorsCallHandler<ReqT, RespT> create(
Iterator<Interceptor> interceptors, CallHandler<ReqT, RespT> callHandler) {
Iterator<ServerInterceptor> interceptors, ServerCallHandler<ReqT, RespT> callHandler) {
return new ProcessInterceptorsCallHandler<ReqT, RespT>(interceptors, callHandler);
}
private Iterator<Interceptor> interceptors;
private final CallHandler<ReqT, RespT> callHandler;
private Iterator<ServerInterceptor> interceptors;
private final ServerCallHandler<ReqT, RespT> callHandler;
private ProcessInterceptorsCallHandler(Iterator<Interceptor> interceptors,
CallHandler<ReqT, RespT> callHandler) {
private ProcessInterceptorsCallHandler(Iterator<ServerInterceptor> interceptors,
ServerCallHandler<ReqT, RespT> callHandler) {
this.interceptors = interceptors;
this.callHandler = callHandler;
}
@Override
public Server.Call.Listener<ReqT> startCall(MethodDescriptor<ReqT, RespT> method,
Server.Call<ReqT, RespT> call) {
public ServerCall.Listener<ReqT> startCall(MethodDescriptor<ReqT, RespT> method,
ServerCall<ReqT, RespT> call) {
if (interceptors != null && interceptors.hasNext()) {
return interceptors.next().interceptCall(method, call, this);
} else {

View File

@ -0,0 +1,39 @@
package com.google.net.stubby;
/** Definition of a method supported by a service. */
public final class ServerMethodDefinition<RequestT, ResponseT> {
private final String name;
private final Marshaller<RequestT> requestMarshaller;
private final Marshaller<ResponseT> responseMarshaller;
private final ServerCallHandler<RequestT, ResponseT> handler;
// ServerMethodDefinition has no form of public construction. It is only created within the
// context of a ServerServiceDefinition.Builder.
ServerMethodDefinition(String name, Marshaller<RequestT> requestMarshaller,
Marshaller<ResponseT> responseMarshaller, ServerCallHandler<RequestT, ResponseT> handler) {
this.name = name;
this.requestMarshaller = requestMarshaller;
this.responseMarshaller = responseMarshaller;
this.handler = handler;
}
/** The simple name of the method. It is not an absolute path. */
public String getName() {
return name;
}
/** Marshaller for deserializing incoming requests. */
public Marshaller<RequestT> getRequestMarshaller() {
return requestMarshaller;
}
/** Marshaller for serializing outgoing responses. */
public Marshaller<ResponseT> getResponseMarshaller() {
return responseMarshaller;
}
/** Handler for incoming calls. */
public ServerCallHandler<RequestT, ResponseT> getServerCallHandler() {
return handler;
}
}

View File

@ -0,0 +1,79 @@
package com.google.net.stubby;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
/** Definition of a service to be exposed via a Server. */
public final class ServerServiceDefinition {
public static Builder builder(String serviceName) {
return new Builder(serviceName);
}
private final String name;
private final ImmutableList<ServerMethodDefinition> methods;
private final ImmutableMap<String, ServerMethodDefinition> methodLookup;
private ServerServiceDefinition(String name, ImmutableList<ServerMethodDefinition> methods,
Map<String, ServerMethodDefinition> methodLookup) {
this.name = name;
this.methods = methods;
this.methodLookup = ImmutableMap.copyOf(methodLookup);
}
/** Simple name of the service. It is not an absolute path. */
public String getName() {
return name;
}
public ImmutableList<ServerMethodDefinition> getMethods() {
return methods;
}
public ServerMethodDefinition getMethod(String name) {
return methodLookup.get(name);
}
/** Builder for constructing Service instances. */
public static final class Builder {
private final String serviceName;
private final ImmutableList.Builder<ServerMethodDefinition> methods = ImmutableList.builder();
private final Map<String, ServerMethodDefinition> methodLookup
= new HashMap<String, ServerMethodDefinition>();
private Builder(String serviceName) {
this.serviceName = serviceName;
}
/**
* Add a method to be supported by the service.
*
* @param name simple name of the method, without the service prefix
* @param requestMarshaller marshaller for deserializing incoming requests
* @param responseMarshaller marshaller for serializing outgoing responses
* @param handler handler for incoming calls
*/
public <ReqT, RespT> Builder addMethod(String name, Marshaller<ReqT> requestMarshaller,
Marshaller<RespT> responseMarshaller, ServerCallHandler<ReqT, RespT> handler) {
Preconditions.checkNotNull(name, "name must not be null");
if (methodLookup.containsKey(name)) {
throw new IllegalStateException("Method by same name already registered");
}
ServerMethodDefinition def = new ServerMethodDefinition<ReqT, RespT>(name,
Preconditions.checkNotNull(requestMarshaller, "requestMarshaller must not be null"),
Preconditions.checkNotNull(responseMarshaller, "responseMarshaller must not be null"),
Preconditions.checkNotNull(handler, "handler must not be null"));
methodLookup.put(name, def);
methods.add(def);
return this;
}
/** Construct new ServerServiceDefinition. */
public ServerServiceDefinition build() {
return new ServerServiceDefinition(serviceName, methods.build(), methodLookup);
}
}
}

View File

@ -7,9 +7,9 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import com.google.net.stubby.Server.CallHandler;
import com.google.net.stubby.Server.MethodDefinition;
import com.google.net.stubby.Server.ServiceDefinition;
import com.google.net.stubby.ServerMethodDefinition;
import com.google.net.stubby.ServerCallHandler;
import com.google.net.stubby.ServerServiceDefinition;
import com.google.net.stubby.HandlerRegistry.Method;
import org.junit.After;
@ -24,15 +24,16 @@ public class MutableHandlerRegistryImplTest {
private MutableHandlerRegistry registry = new MutableHandlerRegistryImpl();
private Marshaller<String> requestMarshaller = mock(Marshaller.class);
private Marshaller<Integer> responseMarshaller = mock(Marshaller.class);
private CallHandler<String, Integer> handler = mock(CallHandler.class);
private ServiceDefinition basicServiceDefinition = ServiceDefinition.builder("basic")
private ServerCallHandler<String, Integer> handler = mock(ServerCallHandler.class);
private ServerServiceDefinition basicServiceDefinition = ServerServiceDefinition.builder("basic")
.addMethod("flow", requestMarshaller, responseMarshaller, handler).build();
private MethodDefinition flowMethodDefinition = basicServiceDefinition.getMethods().get(0);
private ServiceDefinition multiServiceDefinition = ServiceDefinition.builder("multi")
private ServerMethodDefinition flowMethodDefinition = basicServiceDefinition.getMethods().get(0);
private ServerServiceDefinition multiServiceDefinition = ServerServiceDefinition.builder("multi")
.addMethod("couple", requestMarshaller, responseMarshaller, handler)
.addMethod("few", requestMarshaller, responseMarshaller, handler).build();
private MethodDefinition coupleMethodDefinition = multiServiceDefinition.getMethod("couple");
private MethodDefinition fewMethodDefinition = multiServiceDefinition.getMethod("few");
private ServerMethodDefinition coupleMethodDefinition
= multiServiceDefinition.getMethod("couple");
private ServerMethodDefinition fewMethodDefinition = multiServiceDefinition.getMethod("few");
@After
public void makeSureMocksUnused() {
@ -90,9 +91,9 @@ public class MutableHandlerRegistryImplTest {
public void replaceAndLookup() {
assertNull(registry.addService(basicServiceDefinition));
assertNotNull(registry.lookupMethod("/basic.flow"));
ServiceDefinition replaceServiceDefinition = ServiceDefinition.builder("basic")
ServerServiceDefinition replaceServiceDefinition = ServerServiceDefinition.builder("basic")
.addMethod("another", requestMarshaller, responseMarshaller, handler).build();
MethodDefinition anotherMethodDefinition = replaceServiceDefinition.getMethods().get(0);
ServerMethodDefinition anotherMethodDefinition = replaceServiceDefinition.getMethods().get(0);
assertSame(basicServiceDefinition, registry.addService(replaceServiceDefinition));
assertNull(registry.lookupMethod("/basic.flow"));
@ -122,7 +123,7 @@ public class MutableHandlerRegistryImplTest {
@Test
public void removeMissingNameConflictFails() {
assertNull(registry.addService(basicServiceDefinition));
assertFalse(registry.removeService(ServiceDefinition.builder("basic").build()));
assertFalse(registry.removeService(ServerServiceDefinition.builder("basic").build()));
}
@Test
@ -142,6 +143,6 @@ public class MutableHandlerRegistryImplTest {
public void addReturnsPrevious() {
assertNull(registry.addService(basicServiceDefinition));
assertSame(basicServiceDefinition,
registry.addService(ServiceDefinition.builder("basic").build()));
registry.addService(ServerServiceDefinition.builder("basic").build()));
}
}

View File

@ -9,11 +9,10 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import com.google.net.stubby.Server.Interceptor;
import com.google.net.stubby.Server.Call;
import com.google.net.stubby.Server.CallHandler;
import com.google.net.stubby.Server.MethodDefinition;
import com.google.net.stubby.Server.ServiceDefinition;
import com.google.net.stubby.ServerInterceptor;
import com.google.net.stubby.ServerCall;
import com.google.net.stubby.ServerCallHandler;
import com.google.net.stubby.ServerServiceDefinition;
import org.junit.After;
import org.junit.Before;
@ -31,17 +30,18 @@ import java.util.List;
public class ServerInterceptorsTest {
private Marshaller<String> requestMarshaller = mock(Marshaller.class);
private Marshaller<Integer> responseMarshaller = mock(Marshaller.class);
private CallHandler<String, Integer> handler = mock(CallHandler.class);
private Call.Listener<String> listener = mock(Call.Listener.class);
private ServerCallHandler<String, Integer> handler = mock(ServerCallHandler.class);
private ServerCall.Listener<String> listener = mock(ServerCall.Listener.class);
private MethodDescriptor<String, Integer> methodDescriptor = mock(MethodDescriptor.class);
private Call<String, Integer> call = mock(Call.class);
private ServiceDefinition serviceDefinition = ServiceDefinition.builder("basic")
private ServerCall<String, Integer> call = mock(ServerCall.class);
private ServerServiceDefinition serviceDefinition = ServerServiceDefinition.builder("basic")
.addMethod("flow", requestMarshaller, responseMarshaller, handler).build();
@Before
public void setUp() {
Mockito.when(handler.startCall(
Mockito.<MethodDescriptor<String, Integer>>any(), Mockito.<Call<String, Integer>>any()))
Mockito.<MethodDescriptor<String, Integer>>any(),
Mockito.<ServerCall<String, Integer>>any()))
.thenReturn(listener);
}
@ -55,7 +55,7 @@ public class ServerInterceptorsTest {
@Test(expected = NullPointerException.class)
public void npeForNullServiceDefinition() {
ServerInterceptors.intercept(null, Arrays.<Interceptor>asList());
ServerInterceptors.intercept(null, Arrays.<ServerInterceptor>asList());
}
@Test(expected = NullPointerException.class)
@ -65,28 +65,28 @@ public class ServerInterceptorsTest {
@Test(expected = NullPointerException.class)
public void npeForNullInterceptor() {
ServerInterceptors.intercept(serviceDefinition, Arrays.asList((Interceptor) null));
ServerInterceptors.intercept(serviceDefinition, Arrays.asList((ServerInterceptor) null));
}
@Test
public void noop() {
assertSame(serviceDefinition,
ServerInterceptors.intercept(serviceDefinition, Arrays.<Interceptor>asList()));
ServerInterceptors.intercept(serviceDefinition, Arrays.<ServerInterceptor>asList()));
}
@Test
public void multipleInvocationsOfHandler() {
Interceptor interceptor = Mockito.spy(new NoopInterceptor());
ServiceDefinition intercepted
ServerInterceptor interceptor = Mockito.spy(new NoopInterceptor());
ServerServiceDefinition intercepted
= ServerInterceptors.intercept(serviceDefinition, Arrays.asList(interceptor));
assertSame(listener,
intercepted.getMethods().get(0).getCallHandler().startCall(methodDescriptor, call));
intercepted.getMethods().get(0).getServerCallHandler().startCall(methodDescriptor, call));
verify(interceptor).interceptCall(same(methodDescriptor), same(call), anyCallHandler());
verify(handler).startCall(methodDescriptor, call);
verifyNoMoreInteractions(interceptor, handler);
assertSame(listener,
intercepted.getMethods().get(0).getCallHandler().startCall(methodDescriptor, call));
intercepted.getMethods().get(0).getServerCallHandler().startCall(methodDescriptor, call));
verify(interceptor, times(2))
.interceptCall(same(methodDescriptor), same(call), anyCallHandler());
verify(handler, times(2)).startCall(methodDescriptor, call);
@ -95,18 +95,18 @@ public class ServerInterceptorsTest {
@Test
public void correctHandlerCalled() {
CallHandler<String, Integer> handler2 = Mockito.mock(CallHandler.class);
serviceDefinition = ServiceDefinition.builder("basic")
ServerCallHandler<String, Integer> handler2 = Mockito.mock(ServerCallHandler.class);
serviceDefinition = ServerServiceDefinition.builder("basic")
.addMethod("flow", requestMarshaller, responseMarshaller, handler)
.addMethod("flow2", requestMarshaller, responseMarshaller, handler2).build();
ServiceDefinition intercepted = ServerInterceptors.intercept(
serviceDefinition, Arrays.<Interceptor>asList(new NoopInterceptor()));
intercepted.getMethod("flow").getCallHandler().startCall(methodDescriptor, call);
ServerServiceDefinition intercepted = ServerInterceptors.intercept(
serviceDefinition, Arrays.<ServerInterceptor>asList(new NoopInterceptor()));
intercepted.getMethod("flow").getServerCallHandler().startCall(methodDescriptor, call);
verify(handler).startCall(methodDescriptor, call);
verifyNoMoreInteractions(handler);
verifyZeroInteractions(handler2);
intercepted.getMethod("flow2").getCallHandler().startCall(methodDescriptor, call);
intercepted.getMethod("flow2").getServerCallHandler().startCall(methodDescriptor, call);
verify(handler2).startCall(methodDescriptor, call);
verifyNoMoreInteractions(handler);
verifyNoMoreInteractions(handler2);
@ -115,72 +115,73 @@ public class ServerInterceptorsTest {
@Test
public void ordered() {
final List<String> order = new ArrayList<String>();
handler = new CallHandler<String, Integer>() {
handler = new ServerCallHandler<String, Integer>() {
@Override
public Call.Listener<String> startCall(MethodDescriptor<String, Integer> method,
Call<String, Integer> call) {
public ServerCall.Listener<String> startCall(MethodDescriptor<String, Integer> method,
ServerCall<String, Integer> call) {
order.add("handler");
return listener;
}
};
Interceptor interceptor1 = new Interceptor() {
ServerInterceptor interceptor1 = new ServerInterceptor() {
@Override
public <ReqT, RespT> Call.Listener<ReqT> interceptCall(
MethodDescriptor<ReqT, RespT> method, Call<ReqT, RespT> call,
CallHandler<ReqT, RespT> next) {
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
MethodDescriptor<ReqT, RespT> method, ServerCall<ReqT, RespT> call,
ServerCallHandler<ReqT, RespT> next) {
order.add("i1");
return next.startCall(method, call);
}
};
Interceptor interceptor2 = new Interceptor() {
ServerInterceptor interceptor2 = new ServerInterceptor() {
@Override
public <ReqT, RespT> Call.Listener<ReqT> interceptCall(
MethodDescriptor<ReqT, RespT> method, Call<ReqT, RespT> call,
CallHandler<ReqT, RespT> next) {
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
MethodDescriptor<ReqT, RespT> method, ServerCall<ReqT, RespT> call,
ServerCallHandler<ReqT, RespT> next) {
order.add("i2");
return next.startCall(method, call);
}
};
ServiceDefinition serviceDefinition = ServiceDefinition.builder("basic")
ServerServiceDefinition serviceDefinition = ServerServiceDefinition.builder("basic")
.addMethod("flow", requestMarshaller, responseMarshaller, handler).build();
ServiceDefinition intercepted = ServerInterceptors.intercept(
ServerServiceDefinition intercepted = ServerInterceptors.intercept(
serviceDefinition, Arrays.asList(interceptor1, interceptor2));
assertSame(listener,
intercepted.getMethods().get(0).getCallHandler().startCall(methodDescriptor, call));
intercepted.getMethods().get(0).getServerCallHandler().startCall(methodDescriptor, call));
assertEquals(Arrays.asList("i1", "i2", "handler"), order);
}
@Test
public void argumentsPassed() {
final MethodDescriptor<String, Integer> method2 = mock(MethodDescriptor.class);
final Call<String, Integer> call2 = mock(Call.class);
final Call.Listener<String> listener2 = mock(Call.Listener.class);
Interceptor interceptor = new Interceptor() {
final ServerCall<String, Integer> call2 = mock(ServerCall.class);
final ServerCall.Listener<String> listener2 = mock(ServerCall.Listener.class);
ServerInterceptor interceptor = new ServerInterceptor() {
@Override
public <ReqT, RespT> Call.Listener<ReqT> interceptCall(
MethodDescriptor<ReqT, RespT> method, Call<ReqT, RespT> call,
CallHandler<ReqT, RespT> next) {
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
MethodDescriptor<ReqT, RespT> method, ServerCall<ReqT, RespT> call,
ServerCallHandler<ReqT, RespT> next) {
assertSame(method, methodDescriptor);
assertSame(call, ServerInterceptorsTest.this.call);
assertSame(listener, next.startCall((MethodDescriptor) method2, (Call) call2));
return (Call.Listener) listener2;
assertSame(listener, next.startCall((MethodDescriptor) method2, (ServerCall) call2));
return (ServerCall.Listener) listener2;
}
};
ServiceDefinition intercepted = ServerInterceptors.intercept(
ServerServiceDefinition intercepted = ServerInterceptors.intercept(
serviceDefinition, Arrays.asList(interceptor));
assertSame(listener2,
intercepted.getMethods().get(0).getCallHandler().startCall(methodDescriptor, call));
intercepted.getMethods().get(0).getServerCallHandler().startCall(methodDescriptor, call));
verify(handler).startCall(method2, call2);
}
private CallHandler<String, Integer> anyCallHandler() {
return Mockito.<CallHandler<String, Integer>>any();
private ServerCallHandler<String, Integer> anyCallHandler() {
return Mockito.<ServerCallHandler<String, Integer>>any();
}
private static class NoopInterceptor implements Interceptor {
private static class NoopInterceptor implements ServerInterceptor {
@Override
public <ReqT, RespT> Call.Listener<ReqT> interceptCall(MethodDescriptor<ReqT, RespT> method,
Call<ReqT, RespT> call, CallHandler<ReqT, RespT> next) {
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
MethodDescriptor<ReqT, RespT> method, ServerCall<ReqT, RespT> call,
ServerCallHandler<ReqT, RespT> next) {
return next.startCall(method, call);
}
}