ServiceLoader in the JDK
Jesse Glick
jesse.glick at oracle.com
Thu May 24 09:49:53 PDT 2012
On 05/24/2012 11:43 AM, David M. Lloyd wrote:
> Then you're saying that Jigsaw proposes to introduce a new way to represent the current running application other than by class loader (and, by extension, module)?
Well you specify an application module to run from the Java launcher. But this means that various modules will be loaded in the module configuration:
1. The selected module.
2. Its declared dependencies.
3. Transitive dependencies of #2.
4. Perhaps other modules in the library which provide services some of #1-#3 require. Not sure if this has been implemented yet, or if it is rather up to the creator of
the app module to enumerate all desired service provider modules somehow - "requires <providername>;" would I guess work even if the provider module exported no types.
You could "represent" the running application by the application module's ClassLoader, but this could be quite misleading: that loader can see types in #1 and #2,
probably not #3 or (if relevant) #4.
>>> sometimes it's the API module itself
>>
>> Probably pointless, since that would only be able to load services
>> defined in the same module; you might as well statically enumerate them.
>
> This is useful in the case where implementation decisions are made at install time. If an API usually has only a single implementation which makes sense for a platform -
> but it might be replaceable in special circumstances - this would be a good choice. NIO or JAXP might fall into this category.
But then using the ClassLoader of the API module makes it impossible to replace the default implementation:
1. The replacement implementation is by definition not in the API module.
2. It cannot be in a module the API module depends on, or there would be a cycle.
3. So it would be in some other module (which depends on the API module). But then the ClassLoader of the API module cannot see it.
The simplest policy is just to look for any implementations of the service defined in the app.
> it makes sense to define the running application in terms of a module
Yes, that is a reasonable choice. I am saying that defining the application of terms of that module's ClassLoader is however questionable.
>> how [a TCCL] should behave when multiple modules define a
>> class with the same FQN, which ClassLoader cannot represent.
>
> Well clearly a module cannot import more than one class with the same name any more than a class loader can.
Of course. But a modular system can contain multiple classes of the same name, so long as namespaces are consistent. (And the module system permits this as a matter of
policy - TBD whether Jigsaw will.) In such a case the value of ModuleClassLoader.loadClass(String) is well defined for any module in the app, including the "application
module"'s loader, but there is no clear definition of ClassLoader.loadClass(String) for a proposed TCCL, meaning it particular that this TCCL cannot be a ModuleClassLoader.
(In the NetBeans module system, there is a special TCCL for ease of use of "legacy" libraries. If you ask it to load a class which multiple enabled modules define, it
throws a CNFE with a diagnostic message, rather than arbitrarily load one version and maybe cause problems like CCE down the road.)
>> Looking for a subset of those
>> service implementations available within the "current" application is
>> another use case but probably more specialized.
>
> Things like JSF or Hibernate which allow per-application plugins and components
Again this goes back to how the "application" is defined, and whether the java.lang.Class/java.lang.reflect.Module for e.g. Hibernate is shared for all applications in
the JVM or not. There needs to be some easy way for Hibernate code to say "give me all HibernatePlugin instances in the current application". That would be the normal
mode of operation for services. Passing an explicit ClassLoader to load just those HibernatePlugin implementations visible starting from that loader is what I would
consider an advanced usage.
> you're speaking to the inability to use getResources to get a list of implementation names while at the same time associating the
> implementation name with its defining class loader.
Yes, that is one problem. Fortunately it is pretty easily solved with a minor API change in ClassLoader. Of course this only applies to old-style service registration
using META-INF/services/$name, whereas Jigsaw modules declaring "provides service ..." do not use this mechanism (at least currently).
> if you have a list of modules, you can iterate them and use the load-with-classloader variant of ServiceLoader
You mean
for (Module m : somethingTBD()) {
for (Plugin p : ServiceLoader.load(Plugin.class, m.getClassLoader())) {...}
}
? That is one potential workaround - _if_ a ModuleClassLoader refuses to delegate META-INF/** resource loads to its "parents" (i.e. dependencies). If it does delegate
getResources in that way, you will get duplicates. I am not sure if Jigsaw has yet specified the interaction of language accessibility with getResource[s].
More information about the jigsaw-dev
mailing list