ServiceLoader in the JDK
Paul Sandoz
paul.sandoz at oracle.com
Thu May 24 12:53:06 PDT 2012
On May 24, 2012, at 2:40 PM, Jesse Glick wrote:
> 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.
Oh, it's not that bad :-) I think there is a set of behavior for ServiceLoader.load* that does make reasonable sense in modular mode.
> 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);
> }
>
Perhaps ServiceLoader.load(service, this.getClassLoader()) instead.
FWIW i nearly proposed a method ClassLoader.loadService (as you have done in another email).
I think it is valuable to separate out the module service loading functionality from ServiceLoader, e.g. ModuleServiceLoader, then it makes it easier to for us to decide what we can do with it, such use it in ServiceLoader and/or use for any new Class/ClassLoader methods.
> 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.
This container stuff is still all to be determined, but i would expect each container to have it's own configuration (as of Jigsaw today it is not possible to obtain the configuration, or a safe representation of, from a class loader, but it is trivial modification to enable that).
The "permits" complicates matters slightly, if that was not relevant we could look up the services from the configuration.
Paul.
More information about the jigsaw-dev
mailing list