下面列出了怎么用java.util.ServiceLoader.Provider的API类实例代码及写法,或者点击链接到github查看源代码。
/**
* Basic test of the public static provider method. BananaScriptEngine
* defines a provider method that returns the same instance.
*/
@Test
public void testSingleton() {
Optional<Provider<ScriptEngineFactory>> oprovider
= ServiceLoader.load(ScriptEngineFactory.class)
.stream()
.filter(p -> p.type().getName().equals("org.banana.BananaScriptEngineFactory"))
.findFirst();
assertTrue(oprovider.isPresent());
Provider<ScriptEngineFactory> provider = oprovider.get();
// invoke Provider::get twice
ScriptEngineFactory factory1 = provider.get();
ScriptEngineFactory factory2 = provider.get();
assertTrue(factory1 == factory2);
}
/**
* Basic test of stream() to ensure that elements for providers in named
* modules come before elements for providers in unnamed modules.
*/
@Test
public void testStreamOrder() {
List<Class<?>> types = ServiceLoader.load(ScriptEngineFactory.class)
.stream()
.map(Provider::type)
.collect(Collectors.toList());
boolean foundUnnamed = false;
for (Class<?> factoryClass : types) {
if (factoryClass.getModule().isNamed()) {
if (foundUnnamed) {
assertTrue(false, "Named module element after unnamed");
}
} else {
foundUnnamed = true;
}
}
}
/**
* Basic test ServiceLoader.load specifying the platform class loader.
* The providers on the module path and class path should not be located.
*/
@Test
public void testWithPlatformClassLoader() {
ClassLoader pcl = ClassLoader.getPlatformClassLoader();
// iterator
ServiceLoader<ScriptEngineFactory> loader
= ServiceLoader.load(ScriptEngineFactory.class, pcl);
Set<String> names = collectAll(loader)
.stream()
.map(ScriptEngineFactory::getEngineName)
.collect(Collectors.toSet());
assertFalse(names.contains("BananaScriptEngine"));
assertFalse(names.contains("PearScriptEngine"));
// stream
names = ServiceLoader.load(ScriptEngineFactory.class, pcl)
.stream()
.map(Provider::get)
.map(ScriptEngineFactory::getEngineName)
.collect(Collectors.toSet());
assertFalse(names.contains("BananaScriptEngine"));
assertFalse(names.contains("PearScriptEngine"));
}
@Test(dataProvider = "badfactories",
expectedExceptions = ServiceConfigurationError.class)
public void testBadFactory(String testName, String ignore) throws Exception {
Path mods = compileTest(TEST1_MODULE);
// compile the bad factory
Path source = BADFACTORIES_DIR.resolve(testName);
Path output = Files.createTempDirectory(USER_DIR, "tmp");
boolean compiled = CompilerUtils.compile(source, output);
assertTrue(compiled);
// copy the compiled class into the module
Path classFile = Paths.get("p", "ProviderFactory.class");
Files.copy(output.resolve(classFile),
mods.resolve(TEST1_MODULE).resolve(classFile),
StandardCopyOption.REPLACE_EXISTING);
// load providers and instantiate each one
loadProviders(mods, TEST1_MODULE).forEach(Provider::get);
}
@Test(dataProvider = "badproviders",
expectedExceptions = ServiceConfigurationError.class)
public void testBadProvider(String testName, String ignore) throws Exception {
Path mods = compileTest(TEST2_MODULE);
// compile the bad provider
Path source = BADPROVIDERS_DIR.resolve(testName);
Path output = Files.createTempDirectory(USER_DIR, "tmp");
boolean compiled = CompilerUtils.compile(source, output);
assertTrue(compiled);
// copy the compiled class into the module
Path classFile = Paths.get("p", "Provider.class");
Files.copy(output.resolve(classFile),
mods.resolve(TEST2_MODULE).resolve(classFile),
StandardCopyOption.REPLACE_EXISTING);
// load providers and instantiate each one
loadProviders(mods, TEST2_MODULE).forEach(Provider::get);
}
/**
* Uses SPI to find a {@link DataSourceFactory} with the requested name or
* {@code null} if one is not found.
*
* @param name the name of the class that implements the factory
* @return a {@link DataSourceFactory} for {@code name} or {@code null} if one
* is not found
*/
public static DataSourceFactory newFactory(String name) {
if (name == null) throw new IllegalArgumentException("DataSourceFactory name is null");
return ServiceLoader
.load(DataSourceFactory.class)
.stream()
.filter(p -> p.type().getName().equals(name))
.findFirst()
.map(Provider::get)
.orElse(null);
}
private static JMXConnectorServer
getConnectorServerAsService(ClassLoader loader, JMXServiceURL url,
Map<String, ?> map, MBeanServer mbs,
Predicate<Provider<?>> filter)
throws IOException {
final ConnectorFactory<JMXConnectorServerProvider,JMXConnectorServer>
factory = (p) -> p.newJMXConnectorServer(url, map, mbs);
return JMXConnectorFactory.getConnectorAsService(
JMXConnectorServerProvider.class,
loader, url, filter, factory);
}
@Test
public void testConstructorUsingStreamNoPermission() {
ServiceLoader<S1> sl = doPrivileged(loadAction(S1.class), noPermissions());
try {
sl.stream().map(Provider::get).count();
assertTrue(false);
} catch (ServiceConfigurationError e) {
assertTrue(e.getCause() instanceof AccessControlException);
}
}
@Test
public void testFactoryMethodUsingStreamNoPermission() {
ServiceLoader<S2> sl = doPrivileged(loadAction(S2.class), noPermissions());
try {
sl.stream().map(Provider::get).count();
assertTrue(false);
} catch (ServiceConfigurationError e) {
assertTrue(e.getCause() instanceof AccessControlException);
}
}
/**
* Basic test of Provider::type
*/
@Test
public void testProviderType() {
Set<String> types = ServiceLoader.load(ScriptEngineFactory.class)
.stream()
.map(Provider::type)
.map(Class::getName)
.collect(Collectors.toSet());
assertTrue(types.contains("org.banana.BananaScriptEngineFactory"));
assertTrue(types.contains("org.pear.PearScriptEngineFactory"));
}
/**
* Basic test of Provider::get
*/
@Test
public void testProviderGet() {
Set<String> names = ServiceLoader.load(ScriptEngineFactory.class)
.stream()
.map(Provider::get)
.map(ScriptEngineFactory::getEngineName)
.collect(Collectors.toSet());
assertTrue(names.contains("BananaScriptEngine"));
assertTrue(names.contains("PearScriptEngine"));
}
@Test
public void sanityTest1() throws Exception {
Path mods = compileTest(TEST1_MODULE);
List<Provider> list = loadProviders(mods, TEST1_MODULE);
assertTrue(list.size() == 1);
// the provider is a singleton, enforced by the provider factory
Object p1 = list.get(0).get();
Object p2 = list.get(0).get();
assertTrue(p1 != null);
assertTrue(p1 == p2);
}
@Test
public void sanityTest2() throws Exception {
Path mods = compileTest(TEST2_MODULE);
List<Provider> list = loadProviders(mods, TEST2_MODULE);
assertTrue(list.size() == 1);
Object p = list.get(0).get();
assertTrue(p != null);
}
public void testReload() {
ServiceLoader<ScriptEngineFactory> sl = load(ScriptEngineFactory.class);
List<String> names1 = sl.stream()
.map(Provider::get)
.map(ScriptEngineFactory::getEngineName)
.collect(Collectors.toList());
assertFalse(names1.isEmpty());
sl.reload();
List<String> names2 = sl.stream()
.map(Provider::get)
.map(ScriptEngineFactory::getEngineName)
.collect(Collectors.toList());
assertEquals(names1, names2);
}
@Test(expectedExceptions = { ConcurrentModificationException.class })
public void testStreamFindAny() {
ServiceLoader<ScriptEngineFactory> sl = load(ScriptEngineFactory.class);
Stream<Provider<ScriptEngineFactory>> stream = sl.stream();
sl.reload();
stream.findAny();
}
@Test(expectedExceptions = { ConcurrentModificationException.class })
public void testSpliteratorTryAdvance() {
ServiceLoader<ScriptEngineFactory> sl = load(ScriptEngineFactory.class);
Stream<Provider<ScriptEngineFactory>> stream = sl.stream();
Spliterator<Provider<ScriptEngineFactory>> spliterator = stream.spliterator();
sl.reload();
spliterator.tryAdvance(System.out::println);
}
private static Stream<Provider<Plugin>> findDynamicPlugins(Path path) {
var finder = ModuleFinder.of(path);
var moduleNames = finder.findAll().stream().map(ref -> ref.descriptor().name()).collect(toUnmodifiableSet());
var boot = ModuleLayer.boot();
var cf = boot.configuration().resolve(finder, ModuleFinder.of(), moduleNames);
var classLoader = new ClassLoader(Plugins.class.getClassLoader()) { /* empty */ };
var layer = boot.defineModulesWithOneLoader(cf, classLoader);
var serviceLoader = ServiceLoader.load(layer, Plugin.class);
return serviceLoader.stream();
}
private static void loadDynamicPlugins(Path dynamicPluginDir, Consumer<? super Provider<Plugin>> consumer) throws IOException {
if (!Files.isDirectory(dynamicPluginDir)) {
return; // silent return, maybe there is not dynamic plugins
}
try(var stream = Files.list(dynamicPluginDir)) {
stream.flatMap(Plugins::findDynamicPlugins).forEach(consumer);
}
}
public static List<Plugin> getAllPlugins(Path dynamicPluginDir) throws IOException {
var pluginMap = new HashMap<Class<?>, Plugin>();
Consumer<Provider<Plugin>> addToMap = provider -> pluginMap.computeIfAbsent(provider.type(), __ -> provider.get());
// load core plugins
ServiceLoader<Plugin> loader = ServiceLoader.load(Plugin.class, Plugin.class.getClassLoader());
loader.stream().forEach(addToMap);
// load dynamic plugins
loadDynamicPlugins(dynamicPluginDir, addToMap);
return pluginMap.values().stream()
.sorted(Comparator.comparing(Plugin::name)) // have a stable order
.collect(toUnmodifiableList());
}
private MenuEntryService() {
loader = ServiceLoader.load(MenuEntry.class);
menuEntries = loader.stream().map(Provider::get).collect(Collectors.toList());
populateMenuTree(menuEntryTree, menuEntries);
}
private ContextMenuService() {
loader = ServiceLoader.load(ContextMenuEntry.class);
contextMenuEntries = Collections
.unmodifiableList(loader.stream().map(Provider::get).collect(Collectors.toList()));
}
private ToolbarEntryService() {
loader = ServiceLoader.load(ToolbarEntry.class);
toolbarEntries = loader.stream().map(Provider::get).collect(Collectors.toList());
}
@Test
public void testConstructorUsingStreamWithPermission() {
ServiceLoader<S1> sl = doPrivileged(loadAction(S1.class), withPermissions(PERM));
assertTrue(sl.stream().map(Provider::get).count() == 1);
}
@Test
public void testFactoryMethodUsingStreamWithPermission() {
ServiceLoader<S2> sl = doPrivileged(loadAction(S2.class), withPermissions(PERM));
assertTrue(sl.stream().map(Provider::get).count() == 1);
}
/**
* Resolves a test module and loads it into its own layer. ServiceLoader
* is then used to load all providers.
*/
private List<Provider> loadProviders(Path mp, String moduleName) throws Exception {
ModuleFinder finder = ModuleFinder.of(mp);
ModuleLayer bootLayer = ModuleLayer.boot();
Configuration cf = bootLayer.configuration()
.resolveAndBind(finder, ModuleFinder.of(), Set.of(moduleName));
ClassLoader scl = ClassLoader.getSystemClassLoader();
ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, scl);
Class<?> service = layer.findLoader(moduleName).loadClass(TEST_SERVICE);
return ServiceLoader.load(layer, service)
.stream()
.collect(Collectors.toList());
}
/**
* Test a service provider that defines more than one no-args
* public static "provider" method.
*/
@Test(expectedExceptions = ServiceConfigurationError.class)
public void testWithTwoFactoryMethods() throws Exception {
Path mods = compileTest(TEST1_MODULE);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
+ ClassWriter.COMPUTE_FRAMES);
cw.visit(V1_9,
ACC_PUBLIC + ACC_SUPER,
"p/ProviderFactory",
null,
"java/lang/Object",
null);
// public static p.Service provider()
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
"provider",
"()Lp/Service;",
null,
null);
mv.visitTypeInsn(NEW, "p/ProviderFactory$1");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL,
"p/ProviderFactory$1",
"<init>", "()V",
false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// public static p.ProviderFactory$1 provider()
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
"provider",
"()Lp/ProviderFactory$1;",
null,
null);
mv.visitTypeInsn(NEW, "p/ProviderFactory$1");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL,
"p/ProviderFactory$1",
"<init>",
"()V",
false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
cw.visitEnd();
// write the class bytes into the compiled module directory
Path classFile = mods.resolve(TEST1_MODULE)
.resolve("p")
.resolve("ProviderFactory.class");
Files.write(classFile, cw.toByteArray());
// load providers and instantiate each one
loadProviders(mods, TEST1_MODULE).forEach(Provider::get);
}
/**
* Creates a JMXConnector from the first JMXConnectorProvider service
* supporting the given url that can be loaded from the given loader.
* <p>
* Parses the list of JMXConnectorProvider services that can be loaded
* from the given loader, only retaining those that satisfy the given filter.
* Then for each provider, attempts to create a new JMXConnector.
* The first JMXConnector successfully created is returned.
* <p>
* The filter predicate is usually used to either exclude system providers
* or only retain system providers (see isSystemProvider(...) above).
*
* @param loader The ClassLoader to use when looking up an implementation
* of the service. If null, then only installed services will be
* considered.
*
* @param url The JMXServiceURL of the connector for which a provider is
* requested.
*
* @param filter A filter used to exclude or return provider
* implementations. Typically the filter will either exclude
* system services (system default implementations) or only
* retain those.
* This can allow to first look for custom implementations (e.g.
* deployed on the CLASSPATH with META-INF/services) and
* then only default to system implementations.
*
* @throws IOException if no connector could not be instantiated, and
* at least one provider threw an exception that wasn't a
* {@code MalformedURLException} or a {@code JMProviderException}.
*
* @throws JMXProviderException if a provider for the protocol in
* <code>url</code> was found, but couldn't create the connector
* some reason.
*
* @return an instance of JMXConnector if a provider was found from
* which one could be instantiated, {@code null} otherwise.
*/
private static JMXConnector getConnectorAsService(ClassLoader loader,
JMXServiceURL url,
Map<String, ?> map,
Predicate<Provider<?>> filter)
throws IOException {
final ConnectorFactory<JMXConnectorProvider, JMXConnector> factory =
(p) -> p.newJMXConnector(url, map);
return getConnectorAsService(JMXConnectorProvider.class, loader, url,
filter, factory);
}
/**
* Creates a connector from a provider loaded from the ServiceLoader.
* <p>
* The pair (P,C) will be either one of: <br>
* a. (JMXConnectorProvider, JMXConnector) or <br>
* b. (JMXConnectorServerProvider, JMXConnectorServer)
*
* @param providerClass The service type for which an implementation
* should be looked up from the {@code ServiceLoader}. This will
* be either {@code JMXConnectorProvider.class} or
* {@code JMXConnectorServerProvider.class}
*
* @param loader The ClassLoader to use when looking up an implementation
* of the service. If null, then only installed services will be
* considered.
*
* @param url The JMXServiceURL of the connector for which a provider is
* requested.
*
* @param filter A filter used to exclude or return provider
* implementations. Typically the filter will either exclude
* system services (system default implementations) or only
* retain those.
* This can allow to first look for custom implementations (e.g.
* deployed on the CLASSPATH with META-INF/services) and
* then only default to system implementations.
*
* @param factory A functional factory that can attempt to create an
* instance of connector {@code C} from a provider {@code P}.
* Typically, this is a simple wrapper over {@code
* JMXConnectorProvider::newJMXConnector} or {@code
* JMXConnectorProviderServer::newJMXConnectorServer}.
*
* @throws IOException if {@code C} could not be instantiated, and
* at least one provider {@code P} threw an exception that wasn't a
* {@code MalformedURLException} or a {@code JMProviderException}.
*
* @throws JMXProviderException if a provider {@code P} for the protocol in
* <code>url</code> was found, but couldn't create the connector
* {@code C} for some reason.
*
* @return an instance of {@code C} if a provider {@code P} was found from
* which one could be instantiated, {@code null} otherwise.
*/
static <P,C> C getConnectorAsService(Class<P> providerClass,
ClassLoader loader,
JMXServiceURL url,
Predicate<Provider<?>> filter,
ConnectorFactory<P,C> factory)
throws IOException {
// sanity check
if (JMXConnectorProvider.class != providerClass
&& JMXConnectorServerProvider.class != providerClass) {
// should never happen
throw new InternalError("Unsupported service interface: "
+ providerClass.getName());
}
ServiceLoader<P> serviceLoader = loader == null
? ServiceLoader.loadInstalled(providerClass)
: ServiceLoader.load(providerClass, loader);
Stream<Provider<P>> stream = serviceLoader.stream().filter(filter);
ProviderFinder<P,C> finder = new ProviderFinder<>(factory, url);
try {
stream.filter(finder).findFirst();
return finder.get();
} catch (UncheckedIOException e) {
if (e.getCause() instanceof JMXProviderException) {
throw (JMXProviderException) e.getCause();
} else {
throw e;
}
}
}
/**
* Checks whether the given provider is our system provider for
* the RMI connector.
* If providers for additional protocols are added in the future
* then the name of their modules may need to be added here.
* System providers will be loaded only if no other provider is found.
* @param provider the provider to test.
* @return true if this provider is a default system provider.
*/
static boolean isSystemProvider(Provider<?> provider) {
Module providerModule = provider.type().getModule();
return providerModule.isNamed()
&& providerModule.getName().equals("java.management.rmi");
}