Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server builders extend public API #7386

Merged
merged 3 commits into from Sep 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
169 changes: 169 additions & 0 deletions api/src/main/java/io/grpc/ForwardingServerBuilder.java
@@ -0,0 +1,169 @@
/*
* Copyright 2020 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.grpc;

import com.google.common.base.MoreObjects;
import java.io.File;
import java.io.InputStream;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;

/**
* A {@link ServerBuilder} that delegates all its builder method to another builder by default.
*
* @param <T> The type of the subclass extending this abstract class.
* @since 1.33.0
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7393")
public abstract class ForwardingServerBuilder<T extends ForwardingServerBuilder<T>>
extends ServerBuilder<T> {

/** The default constructor. */
protected ForwardingServerBuilder() {}

/**
* This method serves to force sub classes to "hide" this static factory.
*/
public static ServerBuilder<?> forPort(int port) {
throw new UnsupportedOperationException("Subclass failed to hide static factory");
}

/**
* Returns the delegated {@code ServerBuilder}.
*/
protected abstract ServerBuilder<?> delegate();

@Override
public T directExecutor() {
delegate().directExecutor();
return thisT();
}

@Override
public T executor(@Nullable Executor executor) {
delegate().executor(executor);
return thisT();
}

@Override
public T addService(ServerServiceDefinition service) {
delegate().addService(service);
return thisT();
}

@Override
public T addService(BindableService bindableService) {
delegate().addService(bindableService);
return thisT();
}

@Override
public T intercept(ServerInterceptor interceptor) {
delegate().intercept(interceptor);
return thisT();
}

@Override
public T addTransportFilter(ServerTransportFilter filter) {
delegate().addTransportFilter(filter);
return thisT();
}

@Override
public T addStreamTracerFactory(ServerStreamTracer.Factory factory) {
delegate().addStreamTracerFactory(factory);
return thisT();
}

@Override
public T fallbackHandlerRegistry(@Nullable HandlerRegistry fallbackRegistry) {
delegate().fallbackHandlerRegistry(fallbackRegistry);
return thisT();
}

@Override
public T useTransportSecurity(File certChain, File privateKey) {
delegate().useTransportSecurity(certChain, privateKey);
return thisT();
}

@Override
public T useTransportSecurity(InputStream certChain, InputStream privateKey) {
delegate().useTransportSecurity(certChain, privateKey);
return thisT();
}

@Override
public T decompressorRegistry(@Nullable DecompressorRegistry registry) {
delegate().decompressorRegistry(registry);
return thisT();
}

@Override
public T compressorRegistry(@Nullable CompressorRegistry registry) {
delegate().compressorRegistry(registry);
return thisT();
}

@Override
public T handshakeTimeout(long timeout, TimeUnit unit) {
delegate().handshakeTimeout(timeout, unit);
return thisT();
}

@Override
public T maxInboundMessageSize(int bytes) {
delegate().maxInboundMessageSize(bytes);
return thisT();
}

@Override
public T maxInboundMetadataSize(int bytes) {
delegate().maxInboundMetadataSize(bytes);
return thisT();
}

@Override
public T setBinaryLog(BinaryLog binaryLog) {
delegate().setBinaryLog(binaryLog);
return thisT();
}

/**
* Returns the {@link Server} built by the delegate by default. Overriding method can return
* different value.
*/
@Override
public Server build() {
return delegate().build();
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("delegate", delegate()).toString();
}

/**
* Returns the correctly typed version of the builder.
*/
protected final T thisT() {
@SuppressWarnings("unchecked")
T thisT = (T) this;
return thisT;
}
}
83 changes: 83 additions & 0 deletions api/src/test/java/io/grpc/ForwardingServerBuilderTest.java
@@ -0,0 +1,83 @@
/*
* Copyright 2020 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.grpc;

import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;

import com.google.common.base.Defaults;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/**
* Unit tests for {@link ForwardingServerBuilder}.
*/
@RunWith(JUnit4.class)
public class ForwardingServerBuilderTest {
private final ServerBuilder<?> mockDelegate = mock(ServerBuilder.class);
private final ForwardingServerBuilder<?> testServerBuilder = new TestBuilder();

private final class TestBuilder extends ForwardingServerBuilder<TestBuilder> {
@Override
protected ServerBuilder<?> delegate() {
return mockDelegate;
}
}

@Test
public void allMethodsForwarded() throws Exception {
ForwardingTestUtil.testMethodsForwarded(
ServerBuilder.class,
mockDelegate,
testServerBuilder,
Collections.<Method>emptyList());
}

@Test
public void allBuilderMethodsReturnThis() throws Exception {
for (Method method : ServerBuilder.class.getDeclaredMethods()) {
if (Modifier.isStatic(method.getModifiers()) || Modifier.isPrivate(method.getModifiers())) {
continue;
}
if (method.getName().equals("build")) {
continue;
}
Class<?>[] argTypes = method.getParameterTypes();
Object[] args = new Object[argTypes.length];
for (int i = 0; i < argTypes.length; i++) {
args[i] = Defaults.defaultValue(argTypes[i]);
}

Object returnedValue = method.invoke(testServerBuilder, args);

assertThat(returnedValue).isSameInstanceAs(testServerBuilder);
}
}

@Test
public void buildReturnsDelegateBuildByDefault() {
Server server = mock(Server.class);
doReturn(server).when(mockDelegate).build();

assertThat(testServerBuilder.build()).isSameInstanceAs(server);
}
}
Expand Up @@ -22,6 +22,7 @@
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.benchmarks.proto.BenchmarkServiceGrpc;
Expand All @@ -31,7 +32,6 @@
import io.grpc.benchmarks.qps.AsyncServer;
import io.grpc.inprocess.InProcessChannelBuilder;
import io.grpc.inprocess.InProcessServerBuilder;
import io.grpc.internal.AbstractServerImplBuilder;
import io.grpc.netty.NegotiationType;
import io.grpc.netty.NettyChannelBuilder;
import io.grpc.netty.NettyServerBuilder;
Expand Down Expand Up @@ -80,7 +80,7 @@ public enum Transport {

@Setup
public void setUp() throws Exception {
AbstractServerImplBuilder<?> serverBuilder;
ServerBuilder<?> serverBuilder;
ManagedChannelBuilder<?> channelBuilder;
switch (transport) {
case INPROCESS:
Expand Down
41 changes: 33 additions & 8 deletions core/src/main/java/io/grpc/inprocess/InProcessServerBuilder.java
Expand Up @@ -21,11 +21,16 @@
import com.google.common.base.Preconditions;
import io.grpc.Deadline;
import io.grpc.ExperimentalApi;
import io.grpc.ForwardingServerBuilder;
import io.grpc.Internal;
import io.grpc.ServerBuilder;
import io.grpc.ServerStreamTracer;
import io.grpc.internal.AbstractServerImplBuilder;
import io.grpc.internal.FixedObjectPool;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.InternalServer;
import io.grpc.internal.ObjectPool;
import io.grpc.internal.ServerImplBuilder;
import io.grpc.internal.ServerImplBuilder.ClientTransportServersBuilder;
import io.grpc.internal.SharedResourcePool;
import java.io.File;
import java.util.Collections;
Expand Down Expand Up @@ -67,8 +72,7 @@
* </pre>
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1783")
public final class InProcessServerBuilder
extends AbstractServerImplBuilder<InProcessServerBuilder> {
public final class InProcessServerBuilder extends ForwardingServerBuilder<InProcessServerBuilder> {
/**
* Create a server builder that will bind with the given name.
*
Expand All @@ -93,22 +97,40 @@ public static String generateName() {
return UUID.randomUUID().toString();
}

private final ServerImplBuilder serverImplBuilder;
final String name;
int maxInboundMetadataSize = Integer.MAX_VALUE;
ObjectPool<ScheduledExecutorService> schedulerPool =
SharedResourcePool.forResource(GrpcUtil.TIMER_SERVICE);

private InProcessServerBuilder(String name) {
this.name = Preconditions.checkNotNull(name, "name");

final class InProcessClientTransportServersBuilder implements ClientTransportServersBuilder {
@Override
public List<? extends InternalServer> buildClientTransportServers(
List<? extends ServerStreamTracer.Factory> streamTracerFactories) {
return buildTransportServers(streamTracerFactories);
}
}

serverImplBuilder = new ServerImplBuilder(new InProcessClientTransportServersBuilder());

// In-process transport should not record its traffic to the stats module.
// https://github.com/grpc/grpc-java/issues/2284
setStatsRecordStartedRpcs(false);
setStatsRecordFinishedRpcs(false);
serverImplBuilder.setStatsRecordStartedRpcs(false);
serverImplBuilder.setStatsRecordFinishedRpcs(false);
// Disable handshake timeout because it is unnecessary, and can trigger Thread creation that can
// break some environments (like tests).
handshakeTimeout(Long.MAX_VALUE, TimeUnit.SECONDS);
}

@Internal
@Override
protected ServerBuilder<?> delegate() {
return serverImplBuilder;
}

/**
* Provides a custom scheduled executor service.
*
Expand Down Expand Up @@ -140,7 +162,7 @@ public InProcessServerBuilder scheduledExecutorService(
* @since 1.24.0
*/
public InProcessServerBuilder deadlineTicker(Deadline.Ticker ticker) {
setDeadlineTicker(ticker);
serverImplBuilder.setDeadlineTicker(ticker);
return this;
}

Expand All @@ -164,8 +186,7 @@ public InProcessServerBuilder maxInboundMetadataSize(int bytes) {
return this;
}

@Override
protected List<InProcessServer> buildTransportServers(
List<InProcessServer> buildTransportServers(
List<? extends ServerStreamTracer.Factory> streamTracerFactories) {
return Collections.singletonList(new InProcessServer(this, streamTracerFactories));
}
Expand All @@ -174,4 +195,8 @@ protected List<InProcessServer> buildTransportServers(
public InProcessServerBuilder useTransportSecurity(File certChain, File privateKey) {
throw new UnsupportedOperationException("TLS not supported in InProcessServer");
}

void setStatsEnabled(boolean value) {
this.serverImplBuilder.setStatsEnabled(value);
}
}