aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorKun Zhang <zhangkun83@users.noreply.github.com>2018-10-06 12:36:35 -0700
committerGitHub <noreply@github.com>2018-10-06 12:36:35 -0700
commitcc5e3c19df185913d2f9255e815430b7c3f2b331 (patch)
tree4888aedcc4525a41e4a50502ab8905889049fb0b /core
parent0b7043d31ac3f7aa7156b91350d8eb06eed6e3ff (diff)
downloadgrpc-grpc-java-cc5e3c19df185913d2f9255e815430b7c3f2b331.tar.gz
core: ForwardingLoadBalancerHelper (#4911)
This will be used by LoadBalancer plugins that delegates to another, which is what the new request routing (go/grpc-request-routing-design) requires. This will also be used to wrap LoadBalancers to add client-side health-checking functionality.
Diffstat (limited to 'core')
-rw-r--r--core/src/main/java/io/grpc/util/ForwardingLoadBalancerHelper.java95
-rw-r--r--core/src/test/java/io/grpc/ForwardingTestUtil.java54
-rw-r--r--core/src/test/java/io/grpc/util/ForwardingLoadBalancerHelperTest.java65
3 files changed, 210 insertions, 4 deletions
diff --git a/core/src/main/java/io/grpc/util/ForwardingLoadBalancerHelper.java b/core/src/main/java/io/grpc/util/ForwardingLoadBalancerHelper.java
new file mode 100644
index 000000000..e2a9720d4
--- /dev/null
+++ b/core/src/main/java/io/grpc/util/ForwardingLoadBalancerHelper.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018 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.util;
+
+import com.google.common.base.MoreObjects;
+import io.grpc.Attributes;
+import io.grpc.ConnectivityState;
+import io.grpc.EquivalentAddressGroup;
+import io.grpc.ExperimentalApi;
+import io.grpc.LoadBalancer.Subchannel;
+import io.grpc.LoadBalancer.SubchannelPicker;
+import io.grpc.LoadBalancer;
+import io.grpc.ManagedChannel;
+import io.grpc.NameResolver;
+import java.util.List;
+
+@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1771")
+public abstract class ForwardingLoadBalancerHelper extends LoadBalancer.Helper {
+ /**
+ * Returns the underlying helper.
+ */
+ protected abstract LoadBalancer.Helper delegate();
+
+ @Override
+ public Subchannel createSubchannel(EquivalentAddressGroup addrs, Attributes attrs) {
+ return delegate().createSubchannel(addrs, attrs);
+ }
+
+ @Override
+ public Subchannel createSubchannel(List<EquivalentAddressGroup> addrs, Attributes attrs) {
+ return delegate().createSubchannel(addrs, attrs);
+ }
+
+ @Override
+ public void updateSubchannelAddresses(
+ Subchannel subchannel, EquivalentAddressGroup addrs) {
+ delegate().updateSubchannelAddresses(subchannel, addrs);
+ }
+
+ @Override
+ public void updateSubchannelAddresses(
+ Subchannel subchannel, List<EquivalentAddressGroup> addrs) {
+ delegate().updateSubchannelAddresses(subchannel, addrs);
+ }
+
+ @Override
+ public ManagedChannel createOobChannel(EquivalentAddressGroup eag, String authority) {
+ return delegate().createOobChannel(eag, authority);
+ }
+
+ @Override
+ public void updateOobChannelAddresses(ManagedChannel channel, EquivalentAddressGroup eag) {
+ delegate().updateOobChannelAddresses(channel, eag);
+ }
+
+ @Override
+ public void updateBalancingState(
+ ConnectivityState newState, SubchannelPicker newPicker) {
+ delegate().updateBalancingState(newState, newPicker);
+ }
+
+ @Override
+ public void runSerialized(Runnable task) {
+ delegate().runSerialized(task);
+ }
+
+ @Override
+ public NameResolver.Factory getNameResolverFactory() {
+ return delegate().getNameResolverFactory();
+ }
+
+ @Override
+ public String getAuthority() {
+ return delegate().getAuthority();
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("delegate", delegate()).toString();
+ }
+}
diff --git a/core/src/test/java/io/grpc/ForwardingTestUtil.java b/core/src/test/java/io/grpc/ForwardingTestUtil.java
index ce9ca5eb7..e9f1c5b23 100644
--- a/core/src/test/java/io/grpc/ForwardingTestUtil.java
+++ b/core/src/test/java/io/grpc/ForwardingTestUtil.java
@@ -28,6 +28,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
+import javax.annotation.Nullable;
/**
* A util class to help test forwarding classes.
@@ -35,9 +36,8 @@ import java.util.Collection;
public final class ForwardingTestUtil {
/**
* Use reflection to perform a basic sanity test. The forwarding class should forward all public
- * methods to the delegate, except for those in skippedMethods.
- * This does NOT verify that arguments or return values are forwarded properly. It only alerts
- * the developer if a forward method is missing.
+ * methods to the delegate, except for those in skippedMethods. This does NOT verify that
+ * arguments or return values are forwarded properly.
*
* @param delegateClass The class whose methods should be forwarded.
* @param mockDelegate The mockito mock of the delegate class.
@@ -49,6 +49,34 @@ public final class ForwardingTestUtil {
T mockDelegate,
T forwarder,
Collection<Method> skippedMethods) throws Exception {
+ testMethodsForwarded(
+ delegateClass, mockDelegate, forwarder, skippedMethods,
+ new ArgumentProvider() {
+ @Override
+ public Object get(Class<?> clazz) {
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Use reflection to perform a basic sanity test. The forwarding class should forward all public
+ * methods to the delegate, except for those in skippedMethods. This does NOT verify that return
+ * values are forwarded properly, and can only verify the propagation of arguments for which
+ * {@code argProvider} returns distinctive non-null values.
+ *
+ * @param delegateClass The class whose methods should be forwarded.
+ * @param mockDelegate The mockito mock of the delegate class.
+ * @param forwarder The forwarder object that forwards to the mockDelegate.
+ * @param skippedMethods A collection of methods that are skipped by the test.
+ * @param argProvider provides argument to be passed to tested forwarding methods.
+ */
+ public static <T> void testMethodsForwarded(
+ Class<T> delegateClass,
+ T mockDelegate,
+ T forwarder,
+ Collection<Method> skippedMethods,
+ ArgumentProvider argProvider) throws Exception {
assertTrue(mockingDetails(mockDelegate).isMock());
assertFalse(mockingDetails(forwarder).isMock());
@@ -61,7 +89,9 @@ public final class ForwardingTestUtil {
Class<?>[] argTypes = method.getParameterTypes();
Object[] args = new Object[argTypes.length];
for (int i = 0; i < argTypes.length; i++) {
- args[i] = Defaults.defaultValue(argTypes[i]);
+ if ((args[i] = argProvider.get(argTypes[i])) == null) {
+ args[i] = Defaults.defaultValue(argTypes[i]);
+ }
}
method.invoke(forwarder, args);
try {
@@ -85,4 +115,20 @@ public final class ForwardingTestUtil {
assertEquals("Method toString() was not forwarded properly", expected, actual);
}
}
+
+ /**
+ * Provides arguments for forwarded methods tested in {@link #testMethodsForwarded}.
+ */
+ public interface ArgumentProvider {
+ /**
+ * Return an instance of the given class to be used as an argument passed to one method call.
+ * If one method has multiple arguments with the same type, each occurrence will call this
+ * method once. It is recommended that each invocation returns a distinctive object for the
+ * same type, in order to verify that arguments are passed by the tested class correctly.
+ *
+ * @return a value to be passed as an argument. If {@code null}, {@link Default#defaultValue}
+ * will be used.
+ */
+ @Nullable Object get(Class<?> clazz);
+ }
}
diff --git a/core/src/test/java/io/grpc/util/ForwardingLoadBalancerHelperTest.java b/core/src/test/java/io/grpc/util/ForwardingLoadBalancerHelperTest.java
new file mode 100644
index 000000000..ebc883b9a
--- /dev/null
+++ b/core/src/test/java/io/grpc/util/ForwardingLoadBalancerHelperTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 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.util;
+
+import static org.mockito.Mockito.mock;
+
+import io.grpc.EquivalentAddressGroup;
+import io.grpc.ForwardingTestUtil;
+import io.grpc.LoadBalancer;
+import java.lang.reflect.Method;
+import java.net.SocketAddress;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link ForwardingLoadBalancerHelper}. */
+@RunWith(JUnit4.class)
+public class ForwardingLoadBalancerHelperTest {
+ private final LoadBalancer.Helper mockDelegate = mock(LoadBalancer.Helper.class);
+
+ private final class TestHelper extends ForwardingLoadBalancerHelper {
+ @Override
+ protected LoadBalancer.Helper delegate() {
+ return mockDelegate;
+ }
+ }
+
+ @Test
+ public void allMethodsForwarded() throws Exception {
+ final SocketAddress mockAddr = mock(SocketAddress.class);
+ ForwardingTestUtil.testMethodsForwarded(
+ LoadBalancer.Helper.class,
+ mockDelegate,
+ new TestHelper(),
+ Collections.<Method>emptyList(),
+ new ForwardingTestUtil.ArgumentProvider() {
+ @Override
+ public Object get(Class<?> clazz) {
+ if (clazz.equals(EquivalentAddressGroup.class)) {
+ return new EquivalentAddressGroup(Arrays.asList(mockAddr));
+ } else if (clazz.equals(List.class)) {
+ return Collections.<Object>emptyList();
+ }
+ return null;
+ }
+ });
+ }
+}