ServiceLoader in the JDK
Jesse Glick
jesse.glick at oracle.com
Thu May 24 05:40:51 PDT 2012
On 05/24/2012 07:41 AM, Paul Sandoz wrote:
> in module mode the only call that really makes sense is load(Class, ClassLoader) with the appropriate ClassLoader of the module.
This does not make sense at all. load(Class,ClassLoader) is the one factory in ServiceLoader that has a dependable behavior and it should be left alone to behave as it
did in JDK 7, whether running in module mode or not: loader.getResources(PREFIX + service.getName()). It should merely be documented that this variant is not generally
what you want when running in module mode, since there is not likely to be any one ClassLoader which can load META-INF/** from every installed module, nor would this even
be able to find services registered using "provides service" in module-info.class (*). In other words, this method should continue to work as before for old-style JAR
services, and has nothing to do with Jigsaw services.
The next question is what to do with load(Class) and loadInstalled(Class). The former picks up an implicit context in the form of the thread context class loader, which
will again not be useful at all for Jigsaw services, and the latter seems unusably vague in module mode (making as it does a bogus distinction between the "virtual
machine" and the "application"). Probably they both need to be deprecated.
That leaves the question of what Jigsaw-aware code should in fact call. Either introduce a new method in ServiceLoader:
public static <S> ServiceLoader<S> load(Class<S> service, Class<?> reference);
where 'reference' is the caller (**), or generally some class defined by a module which "requires service"; or leave ServiceLoader entirely untouched and define a fresh
API which is actually designed to work with Jigsaw services. You will note that the nonstatic members of ServiceLoader not in Iterable are just reload(), which should be
unnecessary in module mode - if the module system supports dynamic changes, the service loader ought to be able to get notifications of them automatically. (This method
only makes sense when the impl is calling ClassLoader.getResources.) So maybe add to Class:
public <S> Iterable<S> services(Class<S> service) {
Module m = getModule();
return m != null ? m.somehowLoadProviders(service) : ServiceLoader.load(service);
}
which would result in the straightforward idiom:
for (Interface impl : ThisCaller.class.services(Interface.class)) {...}
which would work correctly in module mode and tolerably in classpath mode.
(*) Since these are not accompanied by registrations under PREFIX. Even if they were, and there were a ClassLoader with visibility into all modules, getResources(String)
would still not work for the case that two modules define distinct service impls using the same FQN, which can easily happen in large apps using multiple versions of
third-party libs; I have a longstanding open API RFE in ClassLoader to rectify this.
(**) As in earlier messages I am assuming that a Class and hence a Module instance uniquely identifies a module configuration, i.e. set of candidate providers. Someone
needs to prototype multitenancy/isolates in Jigsaw to make sure this assumption is actually safe. If two applications are running at once in the VM, both of which happen
to use the same version of a particular API module, you would expect that the JVM loads the bytecode for this API just once, yet it is TBD whether there is just one
Module instance for this API (and one Class for each FQN in it), or whether each application gets its own private copy of the Module and Class's. If the latter, Class can
safely be used as a reference key. But if the former, some other reifiable object must be used to represent "the current application", and there needs to be some way to
find this object. These are the kinds of problems you get when using service locators rather than dependency injection.
More information about the jigsaw-dev
mailing list