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:
ejona 2014-08-14 14:17:13 -07:00 committed by Eric Anderson
parent 64eae04d14
commit ba71ee91bf
7 changed files with 602 additions and 31 deletions

View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
/**

View File

@ -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);
}
}
}
}

View File

@ -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()));
}
}

View File

@ -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);
}
}
}