mirror of https://github.com/grpc/grpc-java.git
Swap Server from Builder to HandlerRegistry.
The idea is that Server would be provided a HandlerRegistry at construction time. This has a simplistic implementation of HandlerRegistry. If we like the API, then I can implement a lock-less version as we find need. Most classes are still under Server so that it is obvious what changes were made. Moving things out of Server would be a separate CL. ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=73334671
This commit is contained in:
parent
64eae04d14
commit
ba71ee91bf
|
|
@ -0,0 +1,34 @@
|
|||
package com.google.net.stubby;
|
||||
|
||||
import com.google.net.stubby.Server.MethodDefinition;
|
||||
import com.google.net.stubby.Server.ServiceDefinition;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
/** Registry of services and their methods for dispatching incoming calls. */
|
||||
@ThreadSafe
|
||||
public abstract class HandlerRegistry {
|
||||
/** Lookup full method name, starting with '/'. Returns {@code null} if method not found. */
|
||||
@Nullable
|
||||
public abstract Method lookupMethod(String methodName);
|
||||
|
||||
/** A method definition and its parent's service definition. */
|
||||
public static final class Method {
|
||||
private final ServiceDefinition serviceDef;
|
||||
private final MethodDefinition methodDef;
|
||||
|
||||
public Method(ServiceDefinition serviceDef, MethodDefinition methodDef) {
|
||||
this.serviceDef = serviceDef;
|
||||
this.methodDef = methodDef;
|
||||
}
|
||||
|
||||
public ServiceDefinition getServiceDefinition() {
|
||||
return serviceDef;
|
||||
}
|
||||
|
||||
public MethodDefinition getMethodDefinition() {
|
||||
return methodDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.google.net.stubby;
|
||||
|
||||
import com.google.net.stubby.Server.ServiceDefinition;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
/** Mutable registry of services and their methods for dispatching incoming calls. */
|
||||
@ThreadSafe
|
||||
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);
|
||||
|
||||
/** Returns {@code false} if {@code service} was not registered. */
|
||||
@Nullable
|
||||
public abstract boolean removeService(ServiceDefinition service);
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.google.net.stubby;
|
||||
|
||||
import com.google.net.stubby.Server.MethodDefinition;
|
||||
import com.google.net.stubby.Server.ServiceDefinition;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
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>();
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ServiceDefinition addService(ServiceDefinition service) {
|
||||
return services.put(service.getName(), service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeService(ServiceDefinition service) {
|
||||
return services.remove(service.getName(), service);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Method lookupMethod(String methodName) {
|
||||
methodName = methodName.replace('.', '/');
|
||||
if (!methodName.startsWith("/")) {
|
||||
return null;
|
||||
}
|
||||
methodName = methodName.substring(1);
|
||||
int index = methodName.lastIndexOf("/");
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
ServiceDefinition service = services.get(methodName.substring(0, index));
|
||||
if (service == null) {
|
||||
return null;
|
||||
}
|
||||
MethodDefinition method = service.getMethod(methodName.substring(index + 1));
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
return new Method(service, method);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,14 @@
|
|||
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;
|
||||
|
|
@ -15,24 +19,21 @@ import javax.annotation.concurrent.ThreadSafe;
|
|||
*/
|
||||
@ThreadSafe
|
||||
public interface Server extends Service {
|
||||
/** Builder that Servers are expected to provide for constructing new instances. */
|
||||
abstract class Builder {
|
||||
public abstract Builder addService(ServiceDef service);
|
||||
public abstract Server build();
|
||||
}
|
||||
|
||||
/** Definition of a service to be exposed via a Server. */
|
||||
final class ServiceDef {
|
||||
public static ServiceDef.Builder builder(String serviceName) {
|
||||
return new ServiceDef.Builder(serviceName);
|
||||
final class ServiceDefinition {
|
||||
public static ServiceDefinition.Builder builder(String serviceName) {
|
||||
return new ServiceDefinition.Builder(serviceName);
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final ImmutableList<MethodDef> methods;
|
||||
private final ImmutableList<MethodDefinition> methods;
|
||||
private final ImmutableMap<String, MethodDefinition> methodLookup;
|
||||
|
||||
private ServiceDef(String name, ImmutableList<MethodDef> methods) {
|
||||
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. */
|
||||
|
|
@ -40,14 +41,20 @@ public interface Server extends Service {
|
|||
return name;
|
||||
}
|
||||
|
||||
public ImmutableList<MethodDef> getMethods() {
|
||||
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<MethodDef> methods = ImmutableList.builder();
|
||||
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;
|
||||
|
|
@ -63,29 +70,36 @@ public interface Server extends Service {
|
|||
*/
|
||||
public <ReqT, RespT> Builder addMethod(String name, Marshaller<ReqT> requestMarshaller,
|
||||
Marshaller<RespT> responseMarshaller, CallHandler<ReqT, RespT> handler) {
|
||||
methods.add(
|
||||
new MethodDef<ReqT, RespT>(name, requestMarshaller, responseMarshaller, 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 ServiceDef. */
|
||||
public ServiceDef build() {
|
||||
return new ServiceDef(serviceName, methods.build());
|
||||
/** Construct new ServiceDefinition. */
|
||||
public ServiceDefinition build() {
|
||||
return new ServiceDefinition(serviceName, methods.build(), methodLookup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Definition of a method supported by a service. */
|
||||
final class MethodDef<RequestT, ResponseT> {
|
||||
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;
|
||||
|
||||
// MethodDef has no way of public creation, because all parameters are required. A builder
|
||||
// wouldn't have any methods other than build(). addMethod() can be overriden if we ever need to
|
||||
// extend what MethodDef contains or if we add a Builder or similar.
|
||||
private MethodDef(String name, Marshaller<RequestT> requestMarshaller,
|
||||
// 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;
|
||||
|
|
@ -115,24 +129,47 @@ public interface Server extends Service {
|
|||
}
|
||||
|
||||
/**
|
||||
* Class to begin processing incoming RPCs. Advanced applications and generated code implement
|
||||
* 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 listener for the incoming call. Implementations are free to call methods on {@code
|
||||
* call} before this method has returned.
|
||||
* 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 or returns {@code null}, {@code call} will be
|
||||
* closed with an error.
|
||||
* <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 call object for responding
|
||||
* @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(Server.Call<ReqT, RespT> call,
|
||||
MethodDescriptor<ReqT, RespT> method);
|
||||
Server.Call.Listener<ReqT> startCall(MethodDescriptor<ReqT, RespT> method,
|
||||
Server.Call<ReqT, RespT> call);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
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 java.util.List;
|
||||
import java.util.Iterator;
|
||||
|
||||
/** Utility class for {@link Server.Interceptor}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}.
|
||||
*/
|
||||
public static ServiceDefinition intercept(ServiceDefinition serviceDef,
|
||||
List<Interceptor> interceptors) {
|
||||
Preconditions.checkNotNull(serviceDef);
|
||||
List<Interceptor> immutableInterceptors = ImmutableList.copyOf(interceptors);
|
||||
if (immutableInterceptors.isEmpty()) {
|
||||
return serviceDef;
|
||||
}
|
||||
ServiceDefinition.Builder serviceDefBuilder = ServiceDefinition.builder(serviceDef.getName());
|
||||
for (MethodDefinition<?, ?> 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());
|
||||
serviceDefBuilder.addMethod(method.getName(), method.getRequestMarshaller(),
|
||||
method.getResponseMarshaller(), callHandler);
|
||||
}
|
||||
|
||||
private static class InterceptCallHandler<ReqT, RespT> implements CallHandler<ReqT, RespT> {
|
||||
public static <ReqT, RespT> InterceptCallHandler<ReqT, RespT> create(
|
||||
List<Interceptor> interceptors, CallHandler<ReqT, RespT> callHandler) {
|
||||
return new InterceptCallHandler<ReqT, RespT>(interceptors, callHandler);
|
||||
}
|
||||
|
||||
private final List<Interceptor> interceptors;
|
||||
private final CallHandler<ReqT, RespT> callHandler;
|
||||
|
||||
private InterceptCallHandler(List<Interceptor> interceptors,
|
||||
CallHandler<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) {
|
||||
return ProcessInterceptorsCallHandler.create(interceptors.iterator(), callHandler)
|
||||
.startCall(method, call);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProcessInterceptorsCallHandler<ReqT, RespT>
|
||||
implements CallHandler<ReqT, RespT> {
|
||||
public static <ReqT, RespT> ProcessInterceptorsCallHandler<ReqT, RespT> create(
|
||||
Iterator<Interceptor> interceptors, CallHandler<ReqT, RespT> callHandler) {
|
||||
return new ProcessInterceptorsCallHandler<ReqT, RespT>(interceptors, callHandler);
|
||||
}
|
||||
|
||||
private Iterator<Interceptor> interceptors;
|
||||
private final CallHandler<ReqT, RespT> callHandler;
|
||||
|
||||
private ProcessInterceptorsCallHandler(Iterator<Interceptor> interceptors,
|
||||
CallHandler<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) {
|
||||
if (interceptors != null && interceptors.hasNext()) {
|
||||
return interceptors.next().interceptCall(method, call, this);
|
||||
} else {
|
||||
interceptors = null;
|
||||
return callHandler.startCall(method, call);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
package com.google.net.stubby;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
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.HandlerRegistry.Method;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
/** Unit tests for {@link MutableHandlerRegistryImpl}. */
|
||||
@RunWith(JUnit4.class)
|
||||
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")
|
||||
.addMethod("flow", requestMarshaller, responseMarshaller, handler).build();
|
||||
private MethodDefinition flowMethodDefinition = basicServiceDefinition.getMethods().get(0);
|
||||
private ServiceDefinition multiServiceDefinition = ServiceDefinition.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");
|
||||
|
||||
@After
|
||||
public void makeSureMocksUnused() {
|
||||
Mockito.verifyZeroInteractions(requestMarshaller);
|
||||
Mockito.verifyZeroInteractions(responseMarshaller);
|
||||
Mockito.verifyZeroInteractions(handler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleLookup() {
|
||||
assertNull(registry.addService(basicServiceDefinition));
|
||||
Method method = registry.lookupMethod("/basic.flow");
|
||||
assertSame(flowMethodDefinition, method.getMethodDefinition());
|
||||
assertSame(basicServiceDefinition, method.getServiceDefinition());
|
||||
method = registry.lookupMethod("/basic.flow");
|
||||
assertSame(flowMethodDefinition, method.getMethodDefinition());
|
||||
assertSame(basicServiceDefinition, method.getServiceDefinition());
|
||||
method = registry.lookupMethod("/basic/flow");
|
||||
assertSame(flowMethodDefinition, method.getMethodDefinition());
|
||||
assertSame(basicServiceDefinition, method.getServiceDefinition());
|
||||
|
||||
assertNull(registry.lookupMethod("basic.flow"));
|
||||
assertNull(registry.lookupMethod("/basic.basic"));
|
||||
assertNull(registry.lookupMethod("/flow.flow"));
|
||||
assertNull(registry.lookupMethod("/completely.random"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiServiceLookup() {
|
||||
assertNull(registry.addService(basicServiceDefinition));
|
||||
assertNull(registry.addService(multiServiceDefinition));
|
||||
|
||||
Method method = registry.lookupMethod("/basic.flow");
|
||||
assertSame(flowMethodDefinition, method.getMethodDefinition());
|
||||
assertSame(basicServiceDefinition, method.getServiceDefinition());
|
||||
method = registry.lookupMethod("/multi.couple");
|
||||
assertSame(coupleMethodDefinition, method.getMethodDefinition());
|
||||
assertSame(multiServiceDefinition, method.getServiceDefinition());
|
||||
method = registry.lookupMethod("/multi.few");
|
||||
assertSame(fewMethodDefinition, method.getMethodDefinition());
|
||||
assertSame(multiServiceDefinition, method.getServiceDefinition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeAndLookup() {
|
||||
assertNull(registry.addService(multiServiceDefinition));
|
||||
assertNotNull(registry.lookupMethod("/multi.couple"));
|
||||
assertNotNull(registry.lookupMethod("/multi.few"));
|
||||
assertTrue(registry.removeService(multiServiceDefinition));
|
||||
assertNull(registry.lookupMethod("/multi.couple"));
|
||||
assertNull(registry.lookupMethod("/multi.few"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceAndLookup() {
|
||||
assertNull(registry.addService(basicServiceDefinition));
|
||||
assertNotNull(registry.lookupMethod("/basic.flow"));
|
||||
ServiceDefinition replaceServiceDefinition = ServiceDefinition.builder("basic")
|
||||
.addMethod("another", requestMarshaller, responseMarshaller, handler).build();
|
||||
MethodDefinition anotherMethodDefinition = replaceServiceDefinition.getMethods().get(0);
|
||||
assertSame(basicServiceDefinition, registry.addService(replaceServiceDefinition));
|
||||
|
||||
assertNull(registry.lookupMethod("/basic.flow"));
|
||||
Method method = registry.lookupMethod("/basic.another");
|
||||
assertSame(anotherMethodDefinition, method.getMethodDefinition());
|
||||
assertSame(replaceServiceDefinition, method.getServiceDefinition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeSameSucceeds() {
|
||||
assertNull(registry.addService(basicServiceDefinition));
|
||||
assertTrue(registry.removeService(basicServiceDefinition));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doubleRemoveFails() {
|
||||
assertNull(registry.addService(basicServiceDefinition));
|
||||
assertTrue(registry.removeService(basicServiceDefinition));
|
||||
assertFalse(registry.removeService(basicServiceDefinition));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeMissingFails() {
|
||||
assertFalse(registry.removeService(basicServiceDefinition));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeMissingNameConflictFails() {
|
||||
assertNull(registry.addService(basicServiceDefinition));
|
||||
assertFalse(registry.removeService(ServiceDefinition.builder("basic").build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initialAddReturnsNull() {
|
||||
assertNull(registry.addService(basicServiceDefinition));
|
||||
assertNull(registry.addService(multiServiceDefinition));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addAfterRemoveReturnsNull() {
|
||||
assertNull(registry.addService(basicServiceDefinition));
|
||||
assertTrue(registry.removeService(basicServiceDefinition));
|
||||
assertNull(registry.addService(basicServiceDefinition));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addReturnsPrevious() {
|
||||
assertNull(registry.addService(basicServiceDefinition));
|
||||
assertSame(basicServiceDefinition,
|
||||
registry.addService(ServiceDefinition.builder("basic").build()));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
package com.google.net.stubby;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.same;
|
||||
import static org.mockito.Mockito.times;
|
||||
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 org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Unit tests for {@link ServerInterceptors}. */
|
||||
@RunWith(JUnit4.class)
|
||||
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 MethodDescriptor<String, Integer> methodDescriptor = mock(MethodDescriptor.class);
|
||||
private Call<String, Integer> call = mock(Call.class);
|
||||
private ServiceDefinition serviceDefinition = ServiceDefinition.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()))
|
||||
.thenReturn(listener);
|
||||
}
|
||||
|
||||
@After
|
||||
public void makeSureExpectedMocksUnused() {
|
||||
verifyZeroInteractions(requestMarshaller);
|
||||
verifyZeroInteractions(responseMarshaller);
|
||||
verifyZeroInteractions(listener);
|
||||
verifyZeroInteractions(methodDescriptor);
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void npeForNullServiceDefinition() {
|
||||
ServerInterceptors.intercept(null, Arrays.<Interceptor>asList());
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void npeForNullInterceptorList() {
|
||||
ServerInterceptors.intercept(serviceDefinition, null);
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void npeForNullInterceptor() {
|
||||
ServerInterceptors.intercept(serviceDefinition, Arrays.asList((Interceptor) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noop() {
|
||||
assertSame(serviceDefinition,
|
||||
ServerInterceptors.intercept(serviceDefinition, Arrays.<Interceptor>asList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleInvocationsOfHandler() {
|
||||
Interceptor interceptor = Mockito.spy(new NoopInterceptor());
|
||||
ServiceDefinition intercepted
|
||||
= ServerInterceptors.intercept(serviceDefinition, Arrays.asList(interceptor));
|
||||
assertSame(listener,
|
||||
intercepted.getMethods().get(0).getCallHandler().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));
|
||||
verify(interceptor, times(2))
|
||||
.interceptCall(same(methodDescriptor), same(call), anyCallHandler());
|
||||
verify(handler, times(2)).startCall(methodDescriptor, call);
|
||||
verifyNoMoreInteractions(interceptor, handler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void correctHandlerCalled() {
|
||||
CallHandler<String, Integer> handler2 = Mockito.mock(CallHandler.class);
|
||||
serviceDefinition = ServiceDefinition.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);
|
||||
verify(handler).startCall(methodDescriptor, call);
|
||||
verifyNoMoreInteractions(handler);
|
||||
verifyZeroInteractions(handler2);
|
||||
|
||||
intercepted.getMethod("flow2").getCallHandler().startCall(methodDescriptor, call);
|
||||
verify(handler2).startCall(methodDescriptor, call);
|
||||
verifyNoMoreInteractions(handler);
|
||||
verifyNoMoreInteractions(handler2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ordered() {
|
||||
final List<String> order = new ArrayList<String>();
|
||||
handler = new CallHandler<String, Integer>() {
|
||||
@Override
|
||||
public Call.Listener<String> startCall(MethodDescriptor<String, Integer> method,
|
||||
Call<String, Integer> call) {
|
||||
order.add("handler");
|
||||
return listener;
|
||||
}
|
||||
};
|
||||
Interceptor interceptor1 = new Interceptor() {
|
||||
@Override
|
||||
public <ReqT, RespT> Call.Listener<ReqT> interceptCall(
|
||||
MethodDescriptor<ReqT, RespT> method, Call<ReqT, RespT> call,
|
||||
CallHandler<ReqT, RespT> next) {
|
||||
order.add("i1");
|
||||
return next.startCall(method, call);
|
||||
}
|
||||
};
|
||||
Interceptor interceptor2 = new Interceptor() {
|
||||
@Override
|
||||
public <ReqT, RespT> Call.Listener<ReqT> interceptCall(
|
||||
MethodDescriptor<ReqT, RespT> method, Call<ReqT, RespT> call,
|
||||
CallHandler<ReqT, RespT> next) {
|
||||
order.add("i2");
|
||||
return next.startCall(method, call);
|
||||
}
|
||||
};
|
||||
ServiceDefinition serviceDefinition = ServiceDefinition.builder("basic")
|
||||
.addMethod("flow", requestMarshaller, responseMarshaller, handler).build();
|
||||
ServiceDefinition intercepted = ServerInterceptors.intercept(
|
||||
serviceDefinition, Arrays.asList(interceptor1, interceptor2));
|
||||
assertSame(listener,
|
||||
intercepted.getMethods().get(0).getCallHandler().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() {
|
||||
@Override
|
||||
public <ReqT, RespT> Call.Listener<ReqT> interceptCall(
|
||||
MethodDescriptor<ReqT, RespT> method, Call<ReqT, RespT> call,
|
||||
CallHandler<ReqT, RespT> next) {
|
||||
assertSame(method, methodDescriptor);
|
||||
assertSame(call, ServerInterceptorsTest.this.call);
|
||||
assertSame(listener, next.startCall((MethodDescriptor) method2, (Call) call2));
|
||||
return (Call.Listener) listener2;
|
||||
}
|
||||
};
|
||||
ServiceDefinition intercepted = ServerInterceptors.intercept(
|
||||
serviceDefinition, Arrays.asList(interceptor));
|
||||
assertSame(listener2,
|
||||
intercepted.getMethods().get(0).getCallHandler().startCall(methodDescriptor, call));
|
||||
verify(handler).startCall(method2, call2);
|
||||
}
|
||||
|
||||
private CallHandler<String, Integer> anyCallHandler() {
|
||||
return Mockito.<CallHandler<String, Integer>>any();
|
||||
}
|
||||
|
||||
private static class NoopInterceptor implements Interceptor {
|
||||
@Override
|
||||
public <ReqT, RespT> Call.Listener<ReqT> interceptCall(MethodDescriptor<ReqT, RespT> method,
|
||||
Call<ReqT, RespT> call, CallHandler<ReqT, RespT> next) {
|
||||
return next.startCall(method, call);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue