[Mod-Sys Reqs] Comment on "Services" and "Service declarations"
Jesse Glick
jesse.glick at oracle.com
Fri Jul 15 11:59:40 PDT 2011
Regarding: http://openjdk.java.net/projects/jigsaw/doc/topics/services.html
I do not think it is wise to hardcode support for java.util.ServiceLoader in the module syntax. While ServiceLoader provides a useful minimal mechanism for the service
locator pattern, it is far from the only option. For example, http://sezpoz.java.net/ offers advantages over SL when startup time is a concern, and plenty of people are
using other frameworks (often based on XML manifests) which essentially provide locators that might be useful in a modular app.
Assume that the "Reification" feature offers the low-level plumbing needed by a service locator to identify candidate provider modules and instantiate their services,
e.g. obtaining a ClassLoader for a particular installed module, or enumerating candidate modules. What is then useful for the module system to define is some sort of list
of provided and required abstract "services" (perhaps arbitrary String tokens) in each module. You could omit this feature and still use service locators, but each client
would then be required to deal gracefully with the case that there is no provider; and arguably there are cases where it is better to just forbid the client code to be
linked at all, than to run in a crippled mode where it must throw something like UnsupportedOperationException.
Crucially, I think these lists (of provided and required services) should be defined in compiled metadata, not module-info.java. This would permit annotation processors,
Maven plugins, etc. to generate entries that are implicit in the source code or other project files. For example, supposing an annotation such as
http://bits.netbeans.org/dev/javadoc/org-openide-util-lookup/org/openide/util/lookup/ServiceProvider.html were present in the source tree, its processor would add a
corresponding entry to the "provided" list (the FQN of the service interface). Automatic detection of entries for the "required" list from code using the service locator
pattern may not be feasible, but you could define an annotation to be placed on code that cannot function without at least one provider; a processor would then compile
this into a required list entry.
A refinement to the "required" list would be to make it specific to a Java element visible in the module's public signature (but not to include methods on which @Override
would be permitted). Linking against this module would only force a service provider to be loaded as well when some reference was made to that element. Following the
example from my previous email,
public class Foo {
public String unrelatedMethod() {...}
public interface Saver {
void save(Foo foo, OutputStream os);
}
@RequiresService(Saver.class)
public void save(OutputStream os) {
ServiceLoader.loadInstalled(Saver.class).iterator().next().save(this, os);
}
}
a module (explicitly) calling Foo.save would trigger the loading of foo.jaxp, whereas a module only calling Foo.unrelatedMethod would not. Alternately, Foo.class could be
unconditionally resolved, but the linker would treat any references to 'save' in the absence of a provider by throwing a LinkageError, just as if the return type could
not be resolved. Either behavior could again be supported at build time by an annotation processor, without specific support in the module system for @RequiresService.
A module system feature based on abstract provide/require dependencies could support dependency injection across modules as well as service locators. You would expect
that given
@Inject private Saver saver;
public void save(OutputStream os) {
saver.save(this, os);
}
your DI framework's build-time component would record in module metadata the fact that some module with a Saver implementation must be available at runtime. (The precise
form of the entry might reflect any @Qualifier-annotated constraints, and so on.)
More information about the jigsaw-dev
mailing list