Skip to content

Commit

Permalink
api: Ignore ClassCastExceptions for hard-coded providers
Browse files Browse the repository at this point in the history
This was observed in the Bazel/Blaze build where io.grpc.util is a
separate target from the rest of core. During the build of a library
SecretRoundRobinLoadBalancerProvider was not on the classpath, and the
library was later included into a binary using grpc-core from Maven
Central which includes SecretRoundRobinLoadBalancerProvider.

```
java.util.ServiceConfigurationError: Provider io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider could not be instantiated java.lang.ClassCastException: class io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider cannot be cast to some.app.aaa.aab
```
  • Loading branch information
ejona86 committed May 16, 2022
1 parent ba57a1d commit b5e78d5
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 9 deletions.
17 changes: 14 additions & 3 deletions api/src/main/java/io/grpc/ServiceProviders.java
Expand Up @@ -122,15 +122,26 @@ public static <T> Iterable<T> getCandidatesViaServiceLoader(Class<T> klass, Clas
static <T> Iterable<T> getCandidatesViaHardCoded(Class<T> klass, Iterable<Class<?>> hardcoded) {
List<T> list = new ArrayList<>();
for (Class<?> candidate : hardcoded) {
list.add(create(klass, candidate));
T t = createForHardCoded(klass, candidate);
if (t == null) {
continue;
}
list.add(t);
}
return list;
}

@VisibleForTesting
static <T> T create(Class<T> klass, Class<?> rawClass) {
private static <T> T createForHardCoded(Class<T> klass, Class<?> rawClass) {
try {
return rawClass.asSubclass(klass).getConstructor().newInstance();
} catch (ClassCastException ex) {
// Tools like Proguard that perform obfuscation rewrite strings only when the class they
// reference is known, as otherwise they wouldn't know its new name. This means some
// hard-coded Class.forNames() won't be rewritten. This can cause ClassCastException at
// runtime if the class ends up appearing on the classpath but that class is part of a
// separate copy of grpc. With tools like Maven Shade Plugin the class wouldn't be found at
// all and so would be skipped. We want to skip in this case as well.
return null;
} catch (Throwable t) {
throw new ServiceConfigurationError(
String.format("Provider %s could not be instantiated %s", rawClass.getName(), t), t);
Expand Down
28 changes: 22 additions & 6 deletions api/src/test/java/io/grpc/ServiceProvidersTest.java
Expand Up @@ -215,19 +215,35 @@ public void getCandidatesViaHardCoded_failAtInit_moreCandidates() throws Excepti
}

@Test
public void create_throwsErrorOnMisconfiguration() throws Exception {
class PrivateClass {}
public void getCandidatesViaHardCoded_throwsErrorOnMisconfiguration() throws Exception {
class PrivateClass extends BaseProvider {
private PrivateClass() {
super(true, 5);
}
}

try {
ServiceProviders.create(
ServiceProvidersTestAbstractProvider.class, PrivateClass.class);
ServiceProviders.getCandidatesViaHardCoded(
ServiceProvidersTestAbstractProvider.class,
Collections.<Class<?>>singletonList(PrivateClass.class));
fail("Expected exception");
} catch (ServiceConfigurationError expected) {
assertTrue("Expected ClassCastException cause: " + expected.getCause(),
expected.getCause() instanceof ClassCastException);
assertTrue("Expected NoSuchMethodException cause: " + expected.getCause(),
expected.getCause() instanceof NoSuchMethodException);
}
}

@Test
public void getCandidatesViaHardCoded_skipsWrongClassType() throws Exception {
class RandomClass {}

Iterable<ServiceProvidersTestAbstractProvider> candidates =
ServiceProviders.getCandidatesViaHardCoded(
ServiceProvidersTestAbstractProvider.class,
Collections.<Class<?>>singletonList(RandomClass.class));
assertFalse(candidates.iterator().hasNext());
}

private static class BaseProvider extends ServiceProvidersTestAbstractProvider {
private final boolean isAvailable;
private final int priority;
Expand Down

0 comments on commit b5e78d5

Please sign in to comment.