[Mod-Sys Reqs] Comment on "Optional modules"

Jesse Glick jesse.glick at oracle.com
Fri Jul 15 10:13:31 PDT 2011


Regarding: http://openjdk.java.net/projects/jigsaw/doc/draft-java-module-system-requirements-12#optional-modules

The "guidelines" in http://openjdk.java.net/projects/jigsaw/doc/topics/optional.html make it clear that there are several hazards involved in using optional module 
dependencies effectively; and an IDE or analysis tool would have a hard time enforcing these guidelines, since the developer's notion of what code may refer to the 
optional module is not captured by the language or project layout. This would not seem to fall under the category of features that "encourage best practices".

Certainly for the purpose of quick and dirty modularization of legacy code which uses the idiom of catching ClassNotFoundException to test for the presence of an optional 
dependency, this module system feature is probably required. The same is true of code which exposes types from an optional dependency in its public signature, assuming 
the signature cannot be changed for compatibility reasons.

But I think using optional modules should be considered a mechanism of last resort, and documented accordingly. Refactoring the dependency into a third module invoked via 
a service interface bypasses the need for this mechanism. For example, adopting the example given in optional.html:

module foo {
     requires service com.foo.Foo.Saver;
}
package com.foo;
public class Foo {
     public interface Saver {
         void save(Foo foo, OutputStream os);
     }
     public void save(OutputStream os) {
         ServiceLoader.loadInstalled(Saver.class).iterator().next().save(this, os);
     }
}
module foo.jaxp {
     require foo;
     require jdk.jaxp;
     exports service com.foo.Foo.Saver class com.foo.XMLSaver;
}
package com.foo;
public class XMLSaver implements Saver {
     public void save(Foo foo, OutputStream os) {
         // use TransformerFactory etc.
     }
}

The advantages are several:

1. It is impossible to accidentally use JAXP from other parts of the main 'foo' module.

2. The developer need not be concerned with the intricacies of Java linker semantics. (With the original example, a class Foo that worked as expected in the absence of 
bytecode verification might suddenly throw linkage errors when verification was enabled.)

3. There is no visible reflection, so doing a rename refactoring on the "save" method will work as expected, checked exceptions are enforced, etc.

4. It would be trivial to add an alternate Saver implementation using some lightweight XML serializer not based on JAXP. This would be a valuable option in case the 
reason for making the dependency optional to begin with was that JAXP (incl. Xerces + Xalan) is too big, or has intellectual property issues, etc.

5. The open question about how "to support different versions of a module" becomes moot. Supposing an incompatible change were made to JAXP, you could supply alternate 
modules with Saver implementations which depend on different version ranges of JAXP. An appropriate provider module should be selected at installation time automatically. 
(When using optional dependencies, since 'foo' could import only one version of JAXP to compile against, the other would have to be called quite tediously via reflection.)

6. It is obvious how to modify the code to use a fallback implementation in case no provider is available (after removing "requires service..." from the module 
declaration), or to select the "best" implementation in case several are available, or run all available implementations and somehow aggregate the results where that 
makes sense.



More information about the jigsaw-dev mailing list