Skip to content

Commit 2996207

Browse files
authored
core: generifiy ServiceProvider class (grpc#3886)
Moving all the common code among different service provider classes to one place.
1 parent 417765b commit 2996207

10 files changed

Lines changed: 555 additions & 0 deletions
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2017, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
import com.google.common.annotations.VisibleForTesting;
20+
import java.util.List;
21+
22+
@Internal
23+
public final class InternalServiceProviders {
24+
private InternalServiceProviders() {
25+
}
26+
27+
/**
28+
* Accessor for method.
29+
*/
30+
public static <T> T load(
31+
Class<T> klass,
32+
Iterable<Class<?>> hardcoded,
33+
ClassLoader classLoader,
34+
PriorityAccessor<T> priorityAccessor) {
35+
return ServiceProviders.load(klass, hardcoded, classLoader, priorityAccessor);
36+
}
37+
38+
/**
39+
* Accessor for method.
40+
*/
41+
public static <T> List<T> loadAll(
42+
Class<T> klass,
43+
Iterable<Class<?>> hardCodedClasses,
44+
ClassLoader classLoader,
45+
PriorityAccessor<T> priorityAccessor) {
46+
return ServiceProviders.loadAll(klass, hardCodedClasses, classLoader, priorityAccessor);
47+
}
48+
49+
/**
50+
* Accessor for method.
51+
*/
52+
@VisibleForTesting
53+
public static <T> Iterable<T> getCandidatesViaServiceLoader(Class<T> klass, ClassLoader cl) {
54+
return ServiceProviders.getCandidatesViaServiceLoader(klass, cl);
55+
}
56+
57+
/**
58+
* Accessor for method.
59+
*/
60+
@VisibleForTesting
61+
public static <T> Iterable<T> getCandidatesViaHardCoded(
62+
Class<T> klass, Iterable<Class<?>> hardcoded) {
63+
return ServiceProviders.getCandidatesViaHardCoded(klass, hardcoded);
64+
}
65+
66+
/**
67+
* Accessor for {@link ServiceProviders#isAndroid}.
68+
*/
69+
public static boolean isAndroid(ClassLoader cl) {
70+
return ServiceProviders.isAndroid(cl);
71+
}
72+
73+
public interface PriorityAccessor<T> extends ServiceProviders.PriorityAccessor<T> {}
74+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Copyright 2017, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
import com.google.common.annotations.VisibleForTesting;
20+
import java.util.ArrayList;
21+
import java.util.Collections;
22+
import java.util.Comparator;
23+
import java.util.List;
24+
import java.util.ServiceConfigurationError;
25+
import java.util.ServiceLoader;
26+
27+
final class ServiceProviders {
28+
private ServiceProviders() {
29+
// do not instantiate
30+
}
31+
32+
/**
33+
* If this is not Android, returns the highest priority implementation of the class via
34+
* {@link ServiceLoader}.
35+
* If this is Android, returns an instance of the highest priority class in {@code hardcoded}.
36+
*/
37+
public static <T> T load(
38+
Class<T> klass,
39+
Iterable<Class<?>> hardcoded,
40+
ClassLoader cl,
41+
PriorityAccessor<T> priorityAccessor) {
42+
List<T> candidates = loadAll(klass, hardcoded, cl, priorityAccessor);
43+
if (candidates.isEmpty()) {
44+
return null;
45+
}
46+
return candidates.get(0);
47+
}
48+
49+
/**
50+
* If this is not Android, returns all available implementations discovered via
51+
* {@link ServiceLoader}.
52+
* If this is Android, returns all available implementations in {@code hardcoded}.
53+
* The list is sorted in descending priority order.
54+
*/
55+
public static <T> List<T> loadAll(
56+
Class<T> klass,
57+
Iterable<Class<?>> hardcoded,
58+
ClassLoader cl,
59+
final PriorityAccessor<T> priorityAccessor) {
60+
Iterable<T> candidates;
61+
if (isAndroid(cl)) {
62+
candidates = getCandidatesViaHardCoded(klass, hardcoded);
63+
} else {
64+
candidates = getCandidatesViaServiceLoader(klass, cl);
65+
}
66+
List<T> list = new ArrayList<T>();
67+
for (T current: candidates) {
68+
if (!priorityAccessor.isAvailable(current)) {
69+
continue;
70+
}
71+
list.add(current);
72+
}
73+
74+
// Sort descending based on priority.
75+
Collections.sort(list, Collections.reverseOrder(new Comparator<T>() {
76+
@Override
77+
public int compare(T f1, T f2) {
78+
return priorityAccessor.getPriority(f1) - priorityAccessor.getPriority(f2);
79+
}
80+
}));
81+
return Collections.unmodifiableList(list);
82+
}
83+
84+
/**
85+
* Returns true if the {@link ClassLoader} is for android.
86+
*/
87+
static boolean isAndroid(ClassLoader cl) {
88+
try {
89+
// Specify a class loader instead of null because we may be running under Robolectric
90+
Class.forName("android.app.Application", /*initialize=*/ false, cl);
91+
return true;
92+
} catch (Exception e) {
93+
// If Application isn't loaded, it might as well not be Android.
94+
return false;
95+
}
96+
}
97+
98+
/**
99+
* Loads service providers for the {@code klass} service using {@link ServiceLoader}.
100+
*/
101+
@VisibleForTesting
102+
public static <T> Iterable<T> getCandidatesViaServiceLoader(Class<T> klass, ClassLoader cl) {
103+
Iterable<T> i = ServiceLoader.load(klass, cl);
104+
// Attempt to load using the context class loader and ServiceLoader.
105+
// This allows frameworks like http://aries.apache.org/modules/spi-fly.html to plug in.
106+
if (!i.iterator().hasNext()) {
107+
i = ServiceLoader.load(klass);
108+
}
109+
return i;
110+
}
111+
112+
/**
113+
* Load providers from a hard-coded list. This avoids using getResource(), which has performance
114+
* problems on Android (see https://github.com/grpc/grpc-java/issues/2037).
115+
*/
116+
@VisibleForTesting
117+
static <T> Iterable<T> getCandidatesViaHardCoded(Class<T> klass, Iterable<Class<?>> hardcoded) {
118+
List<T> list = new ArrayList<T>();
119+
for (Class<?> candidate : hardcoded) {
120+
list.add(create(klass, candidate));
121+
}
122+
return list;
123+
}
124+
125+
@VisibleForTesting
126+
static <T> T create(Class<T> klass, Class<?> rawClass) {
127+
try {
128+
return rawClass.asSubclass(klass).getConstructor().newInstance();
129+
} catch (Throwable t) {
130+
throw new ServiceConfigurationError(
131+
String.format("Provider %s could not be instantiated %s", rawClass.getName(), t), t);
132+
}
133+
}
134+
135+
/**
136+
* An interface that allows us to get priority information about a provider.
137+
*/
138+
public interface PriorityAccessor<T> {
139+
/**
140+
* Checks this provider is available for use, taking the current environment into consideration.
141+
* If {@code false}, no other methods are safe to be called.
142+
*/
143+
boolean isAvailable(T provider);
144+
145+
/**
146+
* A priority, from 0 to 10 that this provider should be used, taking the current environment
147+
* into consideration. 5 should be considered the default, and then tweaked based on environment
148+
* detection. A priority of 0 does not imply that the provider wouldn't work; just that it
149+
* should be last in line.
150+
*/
151+
int getPriority(T provider);
152+
}
153+
}

0 commit comments

Comments
 (0)