/* * Copyright 2014, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package io.grpc; import com.google.common.base.Preconditions; import java.io.BufferedInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Utility methods for working with {@link ServerInterceptor}s. */ public final class ServerInterceptors { // Prevent instantiation private ServerInterceptors() {} /** * Create a new {@code ServerServiceDefinition} whose {@link ServerCallHandler}s will call * {@code interceptors} before calling the pre-existing {@code ServerCallHandler}. The first * interceptor will have its {@link ServerInterceptor#interceptCall} called first. * * @param serviceDef the service definition for which to intercept all its methods. * @param interceptors array of interceptors to apply to the service. * @return a wrapped version of {@code serviceDef} with the interceptors applied. */ public static ServerServiceDefinition interceptForward(ServerServiceDefinition serviceDef, ServerInterceptor... interceptors) { return interceptForward(serviceDef, Arrays.asList(interceptors)); } public static ServerServiceDefinition interceptForward(BindableService bindableService, ServerInterceptor... interceptors) { return interceptForward(bindableService.bindService(), Arrays.asList(interceptors)); } /** * Create a new {@code ServerServiceDefinition} whose {@link ServerCallHandler}s will call * {@code interceptors} before calling the pre-existing {@code ServerCallHandler}. The first * interceptor will have its {@link ServerInterceptor#interceptCall} called first. * * @param serviceDef the service definition for which to intercept all its methods. * @param interceptors list of interceptors to apply to the service. * @return a wrapped version of {@code serviceDef} with the interceptors applied. */ public static ServerServiceDefinition interceptForward( ServerServiceDefinition serviceDef, List<? extends ServerInterceptor> interceptors) { List<? extends ServerInterceptor> copy = new ArrayList<ServerInterceptor>(interceptors); Collections.reverse(copy); return intercept(serviceDef, copy); } public static ServerServiceDefinition interceptForward( BindableService bindableService, List<? extends ServerInterceptor> interceptors) { return interceptForward(bindableService.bindService(), interceptors); } /** * Create a new {@code ServerServiceDefinition} whose {@link ServerCallHandler}s will call * {@code interceptors} before calling the pre-existing {@code ServerCallHandler}. The last * interceptor will have its {@link ServerInterceptor#interceptCall} called first. * * @param serviceDef the service definition for which to intercept all its methods. * @param interceptors array of interceptors to apply to the service. * @return a wrapped version of {@code serviceDef} with the interceptors applied. */ public static ServerServiceDefinition intercept(ServerServiceDefinition serviceDef, ServerInterceptor... interceptors) { return intercept(serviceDef, Arrays.asList(interceptors)); } public static ServerServiceDefinition intercept(BindableService bindableService, ServerInterceptor... interceptors) { Preconditions.checkNotNull(bindableService, "bindableService"); return intercept(bindableService.bindService(), Arrays.asList(interceptors)); } /** * Create a new {@code ServerServiceDefinition} whose {@link ServerCallHandler}s will call * {@code interceptors} before calling the pre-existing {@code ServerCallHandler}. The last * interceptor will have its {@link ServerInterceptor#interceptCall} called first. * * @param serviceDef the service definition for which to intercept all its methods. * @param interceptors list of interceptors to apply to the service. * @return a wrapped version of {@code serviceDef} with the interceptors applied. */ public static ServerServiceDefinition intercept(ServerServiceDefinition serviceDef, List<? extends ServerInterceptor> interceptors) { Preconditions.checkNotNull(serviceDef, "serviceDef"); if (interceptors.isEmpty()) { return serviceDef; } ServerServiceDefinition.Builder serviceDefBuilder = ServerServiceDefinition.builder(serviceDef.getServiceDescriptor()); for (ServerMethodDefinition<?, ?> method : serviceDef.getMethods()) { wrapAndAddMethod(serviceDefBuilder, method, interceptors); } return serviceDefBuilder.build(); } public static ServerServiceDefinition intercept(BindableService bindableService, List<? extends ServerInterceptor> interceptors) { Preconditions.checkNotNull(bindableService, "bindableService"); return intercept(bindableService.bindService(), interceptors); } /** * Create a new {@code ServerServiceDefinition} whose {@link MethodDescriptor} serializes to * and from InputStream for all methods. The InputStream is guaranteed return true for * markSupported(). The {@code ServerCallHandler} created will automatically * convert back to the original types for request and response before calling the existing * {@code ServerCallHandler}. Calling this method combined with the intercept methods will * allow the developer to choose whether to intercept messages of InputStream, or the modeled * types of their application. * * @param serviceDef the service definition to convert messages to InputStream * @return a wrapped version of {@code serviceDef} with the InputStream conversion applied. */ @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1712") public static ServerServiceDefinition useInputStreamMessages( final ServerServiceDefinition serviceDef) { final MethodDescriptor.Marshaller<InputStream> marshaller = new MethodDescriptor.Marshaller<InputStream>() { @Override public InputStream stream(final InputStream value) { return value; } @Override public InputStream parse(final InputStream stream) { if (stream.markSupported()) { return stream; } else { return new BufferedInputStream(stream); } } }; return useMarshalledMessages(serviceDef, marshaller); } /** * Create a new {@code ServerServiceDefinition} whose {@link MethodDescriptor} serializes to * and from T for all methods. The {@code ServerCallHandler} created will automatically * convert back to the original types for request and response before calling the existing * {@code ServerCallHandler}. Calling this method combined with the intercept methods will * allow the developer to choose whether to intercept messages of T, or the modeled types * of their application. This can also be chained to allow for interceptors to handle messages * as multiple different T types within the chain if the added cost of serialization is not * a concern. * * @param serviceDef the service definition to convert messages to T * @return a wrapped version of {@code serviceDef} with the T conversion applied. */ @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1712") public static <T> ServerServiceDefinition useMarshalledMessages( final ServerServiceDefinition serviceDef, final MethodDescriptor.Marshaller<T> marshaller) { List<ServerMethodDefinition<?, ?>> wrappedMethods = new ArrayList<ServerMethodDefinition<?, ?>>(); List<MethodDescriptor<?, ?>> wrappedDescriptors = new ArrayList<MethodDescriptor<?, ?>>(); // Wrap the descriptors for (final ServerMethodDefinition<?, ?> definition : serviceDef.getMethods()) { final MethodDescriptor<?, ?> originalMethodDescriptor = definition.getMethodDescriptor(); final MethodDescriptor<T, T> wrappedMethodDescriptor = originalMethodDescriptor.toBuilder(marshaller, marshaller).build(); wrappedDescriptors.add(wrappedMethodDescriptor); wrappedMethods.add(wrapMethod(definition, wrappedMethodDescriptor)); } // Build the new service descriptor final ServerServiceDefinition.Builder serviceBuilder = ServerServiceDefinition .builder(new ServiceDescriptor(serviceDef.getServiceDescriptor().getName(), wrappedDescriptors)); // Create the new service definiton. for (ServerMethodDefinition<?, ?> definition : wrappedMethods) { serviceBuilder.addMethod(definition); } return serviceBuilder.build(); } private static <ReqT, RespT> void wrapAndAddMethod( ServerServiceDefinition.Builder serviceDefBuilder, ServerMethodDefinition<ReqT, RespT> method, List<? extends ServerInterceptor> interceptors) { ServerCallHandler<ReqT, RespT> callHandler = method.getServerCallHandler(); for (ServerInterceptor interceptor : interceptors) { callHandler = InterceptCallHandler.create(interceptor, callHandler); } serviceDefBuilder.addMethod(method.withServerCallHandler(callHandler)); } private static class InterceptCallHandler<ReqT, RespT> implements ServerCallHandler<ReqT, RespT> { public static <ReqT, RespT> InterceptCallHandler<ReqT, RespT> create( ServerInterceptor interceptor, ServerCallHandler<ReqT, RespT> callHandler) { return new InterceptCallHandler<ReqT, RespT>(interceptor, callHandler); } private final ServerInterceptor interceptor; private final ServerCallHandler<ReqT, RespT> callHandler; private InterceptCallHandler(ServerInterceptor interceptor, ServerCallHandler<ReqT, RespT> callHandler) { this.interceptor = Preconditions.checkNotNull(interceptor, "interceptor"); this.callHandler = callHandler; } @Override public ServerCall.Listener<ReqT> startCall( ServerCall<ReqT, RespT> call, Metadata headers) { return interceptor.interceptCall(call, headers, callHandler); } } private static <OReqT, ORespT, WReqT, WRespT> ServerMethodDefinition<WReqT, WRespT> wrapMethod( final ServerMethodDefinition<OReqT, ORespT> definition, final MethodDescriptor<WReqT, WRespT> wrappedMethod) { final ServerCallHandler<WReqT, WRespT> wrappedHandler = wrapHandler( definition.getServerCallHandler(), definition.getMethodDescriptor(), wrappedMethod); return ServerMethodDefinition.create(wrappedMethod, wrappedHandler); } private static <OReqT, ORespT, WReqT, WRespT> ServerCallHandler<WReqT, WRespT> wrapHandler( final ServerCallHandler<OReqT, ORespT> originalHandler, final MethodDescriptor<OReqT, ORespT> originalMethod, final MethodDescriptor<WReqT, WRespT> wrappedMethod) { return new ServerCallHandler<WReqT, WRespT>() { @Override public ServerCall.Listener<WReqT> startCall( final ServerCall<WReqT, WRespT> call, final Metadata headers) { final ServerCall<OReqT, ORespT> unwrappedCall = new PartialForwardingServerCall<OReqT, ORespT>() { @Override protected ServerCall<WReqT, WRespT> delegate() { return call; } @Override public void sendMessage(ORespT message) { final InputStream is = originalMethod.streamResponse(message); final WRespT wrappedMessage = wrappedMethod.parseResponse(is); delegate().sendMessage(wrappedMessage); } @Override public MethodDescriptor<OReqT, ORespT> getMethodDescriptor() { return originalMethod; } }; final ServerCall.Listener<OReqT> originalListener = originalHandler .startCall(unwrappedCall, headers); return new PartialForwardingServerCallListener<WReqT>() { @Override protected ServerCall.Listener<OReqT> delegate() { return originalListener; } @Override public void onMessage(WReqT message) { final InputStream is = wrappedMethod.streamRequest(message); final OReqT originalMessage = originalMethod.parseRequest(is); delegate().onMessage(originalMessage); } }; } }; } }