mirror of https://github.com/grpc/grpc-java.git
core: Use Class.forName(String) in provider for Android
Class.forName(String) is understood by ProGuard, removing the need for manual ProGuard configuration and allows ProGuard to rename the provider classes. Previously the provider classes could not be renamed. Fixes #2633
This commit is contained in:
parent
8572f5ff6b
commit
d325919f62
|
|
@ -18,5 +18,3 @@
|
||||||
-dontwarn sun.reflect.**
|
-dontwarn sun.reflect.**
|
||||||
# Ignores: can't find referenced class javax.lang.model.element.Modifier
|
# Ignores: can't find referenced class javax.lang.model.element.Modifier
|
||||||
-dontwarn com.google.errorprone.annotations.**
|
-dontwarn com.google.errorprone.annotations.**
|
||||||
-keep class io.grpc.internal.DnsNameResolverProvider
|
|
||||||
-keep class io.grpc.okhttp.OkHttpChannelProvider
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ public abstract class ManagedChannelProvider {
|
||||||
static ManagedChannelProvider load(ClassLoader classLoader) {
|
static ManagedChannelProvider load(ClassLoader classLoader) {
|
||||||
Iterable<ManagedChannelProvider> candidates;
|
Iterable<ManagedChannelProvider> candidates;
|
||||||
if (isAndroid()) {
|
if (isAndroid()) {
|
||||||
candidates = getCandidatesViaHardCoded(classLoader);
|
candidates = getCandidatesViaHardCoded();
|
||||||
} else {
|
} else {
|
||||||
candidates = getCandidatesViaServiceLoader(classLoader);
|
candidates = getCandidatesViaServiceLoader(classLoader);
|
||||||
}
|
}
|
||||||
|
|
@ -79,16 +79,18 @@ public abstract class ManagedChannelProvider {
|
||||||
* be used on Android is free to be added here.
|
* be used on Android is free to be added here.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static Iterable<ManagedChannelProvider> getCandidatesViaHardCoded(
|
public static Iterable<ManagedChannelProvider> getCandidatesViaHardCoded() {
|
||||||
ClassLoader classLoader) {
|
// Class.forName(String) is used to remove the need for ProGuard configuration. Note that
|
||||||
|
// ProGuard does not detect usages of Class.forName(String, boolean, ClassLoader):
|
||||||
|
// https://sourceforge.net/p/proguard/bugs/418/
|
||||||
List<ManagedChannelProvider> list = new ArrayList<ManagedChannelProvider>();
|
List<ManagedChannelProvider> list = new ArrayList<ManagedChannelProvider>();
|
||||||
try {
|
try {
|
||||||
list.add(create(Class.forName("io.grpc.okhttp.OkHttpChannelProvider", true, classLoader)));
|
list.add(create(Class.forName("io.grpc.okhttp.OkHttpChannelProvider")));
|
||||||
} catch (ClassNotFoundException ex) {
|
} catch (ClassNotFoundException ex) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
list.add(create(Class.forName("io.grpc.netty.NettyChannelProvider", true, classLoader)));
|
list.add(create(Class.forName("io.grpc.netty.NettyChannelProvider")));
|
||||||
} catch (ClassNotFoundException ex) {
|
} catch (ClassNotFoundException ex) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ public abstract class NameResolverProvider extends NameResolver.Factory {
|
||||||
static List<NameResolverProvider> load(ClassLoader classLoader) {
|
static List<NameResolverProvider> load(ClassLoader classLoader) {
|
||||||
Iterable<NameResolverProvider> candidates;
|
Iterable<NameResolverProvider> candidates;
|
||||||
if (isAndroid()) {
|
if (isAndroid()) {
|
||||||
candidates = getCandidatesViaHardCoded(classLoader);
|
candidates = getCandidatesViaHardCoded();
|
||||||
} else {
|
} else {
|
||||||
candidates = getCandidatesViaServiceLoader(classLoader);
|
candidates = getCandidatesViaServiceLoader(classLoader);
|
||||||
}
|
}
|
||||||
|
|
@ -83,11 +83,13 @@ public abstract class NameResolverProvider extends NameResolver.Factory {
|
||||||
* be used on Android is free to be added here.
|
* be used on Android is free to be added here.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static Iterable<NameResolverProvider> getCandidatesViaHardCoded(ClassLoader classLoader) {
|
public static Iterable<NameResolverProvider> getCandidatesViaHardCoded() {
|
||||||
|
// Class.forName(String) is used to remove the need for ProGuard configuration. Note that
|
||||||
|
// ProGuard does not detect usages of Class.forName(String, boolean, ClassLoader):
|
||||||
|
// https://sourceforge.net/p/proguard/bugs/418/
|
||||||
List<NameResolverProvider> list = new ArrayList<NameResolverProvider>();
|
List<NameResolverProvider> list = new ArrayList<NameResolverProvider>();
|
||||||
try {
|
try {
|
||||||
list.add(create(
|
list.add(create(Class.forName("io.grpc.internal.DnsNameResolverProvider")));
|
||||||
Class.forName("io.grpc.internal.DnsNameResolverProvider", true, classLoader)));
|
|
||||||
} catch (ClassNotFoundException ex) {
|
} catch (ClassNotFoundException ex) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,10 @@ import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.ServiceConfigurationError;
|
import java.util.ServiceConfigurationError;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
@ -52,15 +55,22 @@ public class ManagedChannelProviderTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getCandidatesViaHardCoded_usesProvidedClassLoader() {
|
public void getCandidatesViaHardCoded_triesToLoadClasses() throws Exception {
|
||||||
|
ClassLoader cl = getClass().getClassLoader();
|
||||||
final RuntimeException toThrow = new RuntimeException();
|
final RuntimeException toThrow = new RuntimeException();
|
||||||
try {
|
cl = new ClassLoader(cl) {
|
||||||
ManagedChannelProvider.getCandidatesViaHardCoded(new ClassLoader() {
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> loadClass(String name) {
|
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
|
if (name.startsWith("io.grpc.netty.") || name.startsWith("io.grpc.okhttp.")) {
|
||||||
throw toThrow;
|
throw toThrow;
|
||||||
|
} else {
|
||||||
|
return super.loadClass(name, resolve);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
cl = new StaticTestingClassLoader(cl, Pattern.compile("io\\.grpc\\.[^.]*"));
|
||||||
|
try {
|
||||||
|
invokeGetCandidatesViaHardCoded(cl);
|
||||||
fail("Expected exception");
|
fail("Expected exception");
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
assertSame(toThrow, ex);
|
assertSame(toThrow, ex);
|
||||||
|
|
@ -68,14 +78,20 @@ public class ManagedChannelProviderTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getCandidatesViaHardCoded_ignoresMissingClasses() {
|
public void getCandidatesViaHardCoded_ignoresMissingClasses() throws Exception {
|
||||||
Iterable<ManagedChannelProvider> i =
|
ClassLoader cl = getClass().getClassLoader();
|
||||||
ManagedChannelProvider.getCandidatesViaHardCoded(new ClassLoader() {
|
cl = new ClassLoader(cl) {
|
||||||
@Override
|
@Override
|
||||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
|
if (name.startsWith("io.grpc.netty.") || name.startsWith("io.grpc.okhttp.")) {
|
||||||
throw new ClassNotFoundException();
|
throw new ClassNotFoundException();
|
||||||
|
} else {
|
||||||
|
return super.loadClass(name, resolve);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
cl = new StaticTestingClassLoader(cl, Pattern.compile("io\\.grpc\\.[^.]*"));
|
||||||
|
Iterable<?> i = invokeGetCandidatesViaHardCoded(cl);
|
||||||
assertFalse("Iterator should be empty", i.iterator().hasNext());
|
assertFalse("Iterator should be empty", i.iterator().hasNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,6 +108,20 @@ public class ManagedChannelProviderTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Iterable<?> invokeGetCandidatesViaHardCoded(ClassLoader cl) throws Exception {
|
||||||
|
// An error before the invoke likely means there is a bug in the test
|
||||||
|
Class<?> klass = Class.forName(ManagedChannelProvider.class.getName(), true, cl);
|
||||||
|
Method getCandidatesViaHardCoded = klass.getMethod("getCandidatesViaHardCoded");
|
||||||
|
try {
|
||||||
|
return (Iterable<?>) getCandidatesViaHardCoded.invoke(null);
|
||||||
|
} catch (InvocationTargetException ex) {
|
||||||
|
if (ex.getCause() instanceof Exception) {
|
||||||
|
throw (Exception) ex.getCause();
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class BaseProvider extends ManagedChannelProvider {
|
private static class BaseProvider extends ManagedChannelProvider {
|
||||||
private final boolean isAvailable;
|
private final boolean isAvailable;
|
||||||
private final int priority;
|
private final int priority;
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,17 @@ import static org.junit.Assert.fail;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
import io.grpc.internal.DnsNameResolverProvider;
|
import io.grpc.internal.DnsNameResolverProvider;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.ServiceConfigurationError;
|
import java.util.ServiceConfigurationError;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
@ -120,15 +127,24 @@ public class NameResolverProviderTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getCandidatesViaHardCoded_usesProvidedClassLoader() {
|
public void getCandidatesViaHardCoded_triesToLoadClasses() throws Exception {
|
||||||
|
ClassLoader cl = getClass().getClassLoader();
|
||||||
final RuntimeException toThrow = new RuntimeException();
|
final RuntimeException toThrow = new RuntimeException();
|
||||||
try {
|
// Prevent DnsNameResolverProvider from being known
|
||||||
NameResolverProvider.getCandidatesViaHardCoded(new ClassLoader() {
|
cl = new FilteringClassLoader(cl, serviceFile);
|
||||||
|
cl = new ClassLoader(cl) {
|
||||||
@Override
|
@Override
|
||||||
public Class<?> loadClass(String name) {
|
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
|
if (name.startsWith("io.grpc.internal.")) {
|
||||||
throw toThrow;
|
throw toThrow;
|
||||||
|
} else {
|
||||||
|
return super.loadClass(name, resolve);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
cl = new StaticTestingClassLoader(cl, Pattern.compile("io\\.grpc\\.[^.]*"));
|
||||||
|
try {
|
||||||
|
invokeGetCandidatesViaHardCoded(cl);
|
||||||
fail("Expected exception");
|
fail("Expected exception");
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
assertSame(toThrow, ex);
|
assertSame(toThrow, ex);
|
||||||
|
|
@ -136,14 +152,22 @@ public class NameResolverProviderTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getCandidatesViaHardCoded_ignoresMissingClasses() {
|
public void getCandidatesViaHardCoded_ignoresMissingClasses() throws Exception {
|
||||||
Iterable<NameResolverProvider> i =
|
ClassLoader cl = getClass().getClassLoader();
|
||||||
NameResolverProvider.getCandidatesViaHardCoded(new ClassLoader() {
|
// Prevent DnsNameResolverProvider from being known
|
||||||
|
cl = new FilteringClassLoader(cl, serviceFile);
|
||||||
|
cl = new ClassLoader(cl) {
|
||||||
@Override
|
@Override
|
||||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
|
if (name.startsWith("io.grpc.internal.")) {
|
||||||
throw new ClassNotFoundException();
|
throw new ClassNotFoundException();
|
||||||
|
} else {
|
||||||
|
return super.loadClass(name, resolve);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
cl = new StaticTestingClassLoader(cl, Pattern.compile("io\\.grpc\\.[^.]*"));
|
||||||
|
Iterable<?> i = invokeGetCandidatesViaHardCoded(cl);
|
||||||
assertFalse("Iterator should be empty", i.iterator().hasNext());
|
assertFalse("Iterator should be empty", i.iterator().hasNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,6 +184,53 @@ public class NameResolverProviderTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Iterable<?> invokeGetCandidatesViaHardCoded(ClassLoader cl) throws Exception {
|
||||||
|
// An error before the invoke likely means there is a bug in the test
|
||||||
|
Class<?> klass = Class.forName(NameResolverProvider.class.getName(), true, cl);
|
||||||
|
Method getCandidatesViaHardCoded = klass.getMethod("getCandidatesViaHardCoded");
|
||||||
|
try {
|
||||||
|
return (Iterable<?>) getCandidatesViaHardCoded.invoke(null);
|
||||||
|
} catch (InvocationTargetException ex) {
|
||||||
|
if (ex.getCause() instanceof Exception) {
|
||||||
|
throw (Exception) ex.getCause();
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FilteringClassLoader extends ClassLoader {
|
||||||
|
private final String resource;
|
||||||
|
|
||||||
|
public FilteringClassLoader(ClassLoader parent, String resource) {
|
||||||
|
super(parent);
|
||||||
|
this.resource = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getResource(String name) {
|
||||||
|
if (resource.equals(name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return super.getResource(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<URL> getResources(String name) throws IOException {
|
||||||
|
if (resource.equals(name)) {
|
||||||
|
return new Enumeration<URL>() {
|
||||||
|
@Override public boolean hasMoreElements() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public URL nextElement() {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return super.getResources(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class BaseProvider extends NameResolverProvider {
|
private static class BaseProvider extends NameResolverProvider {
|
||||||
private final boolean isAvailable;
|
private final boolean isAvailable;
|
||||||
private final int priority;
|
private final int priority;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017, gRPC Authors All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.Preconditions;
|
||||||
|
import io.grpc.internal.IoUtils;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class loader that can be used to repeatedly trigger static initialization of a class. A new
|
||||||
|
* instance is required per test.
|
||||||
|
*/
|
||||||
|
public final class StaticTestingClassLoader extends ClassLoader {
|
||||||
|
private final Pattern classesToDefine;
|
||||||
|
|
||||||
|
public StaticTestingClassLoader(ClassLoader parent, Pattern classesToDefine) {
|
||||||
|
super(parent);
|
||||||
|
this.classesToDefine = Preconditions.checkNotNull(classesToDefine, "classesToDefine");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||||
|
if (!classesToDefine.matcher(name).matches()) {
|
||||||
|
throw new ClassNotFoundException(name);
|
||||||
|
}
|
||||||
|
InputStream is = getResourceAsStream(name.replace('.', '/') + ".class");
|
||||||
|
if (is == null) {
|
||||||
|
throw new ClassNotFoundException(name);
|
||||||
|
}
|
||||||
|
byte[] b;
|
||||||
|
try {
|
||||||
|
b = IoUtils.toByteArray(is);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new ClassNotFoundException(name, ex);
|
||||||
|
}
|
||||||
|
return defineClass(name, b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
|
// Reverse normal loading order; check this class loader before its parent
|
||||||
|
synchronized (getClassLoadingLock(name)) {
|
||||||
|
Class<?> klass = findLoadedClass(name);
|
||||||
|
if (klass == null) {
|
||||||
|
try {
|
||||||
|
klass = findClass(name);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// This ClassLoader doesn't know a class with that name; that's part of normal operation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (klass == null) {
|
||||||
|
klass = super.loadClass(name, false);
|
||||||
|
}
|
||||||
|
if (resolve) {
|
||||||
|
resolveClass(klass);
|
||||||
|
}
|
||||||
|
return klass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -46,8 +46,7 @@ public class DnsNameResolverProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void providedHardCoded() {
|
public void providedHardCoded() {
|
||||||
for (NameResolverProvider current
|
for (NameResolverProvider current : NameResolverProvider.getCandidatesViaHardCoded()) {
|
||||||
: NameResolverProvider.getCandidatesViaHardCoded(getClass().getClassLoader())) {
|
|
||||||
if (current instanceof DnsNameResolverProvider) {
|
if (current instanceof DnsNameResolverProvider) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,3 @@
|
||||||
-dontwarn javax.naming.**
|
-dontwarn javax.naming.**
|
||||||
-dontwarn okio.**
|
-dontwarn okio.**
|
||||||
-dontwarn sun.misc.Unsafe
|
-dontwarn sun.misc.Unsafe
|
||||||
-keep class io.grpc.internal.DnsNameResolverProvider
|
|
||||||
-keep class io.grpc.okhttp.OkHttpChannelProvider
|
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,3 @@
|
||||||
-dontwarn okio.**
|
-dontwarn okio.**
|
||||||
# Ignores: can't find referenced class javax.lang.model.element.Modifier
|
# Ignores: can't find referenced class javax.lang.model.element.Modifier
|
||||||
-dontwarn com.google.errorprone.annotations.**
|
-dontwarn com.google.errorprone.annotations.**
|
||||||
-keep class io.grpc.internal.DnsNameResolverProvider
|
|
||||||
-keep class io.grpc.okhttp.OkHttpChannelProvider
|
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,7 @@ public class NettyChannelProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void providedHardCoded() {
|
public void providedHardCoded() {
|
||||||
for (ManagedChannelProvider current
|
for (ManagedChannelProvider current : ManagedChannelProvider.getCandidatesViaHardCoded()) {
|
||||||
: ManagedChannelProvider.getCandidatesViaHardCoded(getClass().getClassLoader())) {
|
|
||||||
if (current instanceof NettyChannelProvider) {
|
if (current instanceof NettyChannelProvider) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,7 @@ public class OkHttpChannelProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void providedHardCoded() {
|
public void providedHardCoded() {
|
||||||
for (ManagedChannelProvider current
|
for (ManagedChannelProvider current : ManagedChannelProvider.getCandidatesViaHardCoded()) {
|
||||||
: ManagedChannelProvider.getCandidatesViaHardCoded(getClass().getClassLoader())) {
|
|
||||||
if (current instanceof OkHttpChannelProvider) {
|
if (current instanceof OkHttpChannelProvider) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue