This is the second part of my investigation into Java 9 with Liberty. You can read about the first part here: A first look at Liberty on Java 9.
To recap, my experiment is organized into two phases:
- Phase 1: Bring up WebSphere Liberty on Java 9 using any overrides necessary, with a focus on making as few code changes as possible.
- Phase 2: Examine how Liberty can exploit the Java Platform Module System (JPMS). Specifically, the feasibility of organizing the OSGi-based Liberty runtime and applications running on Liberty into JPMS modules.
Phase 2: Liberty exploitation of Java 9
In Phase 1, Liberty and its applications were operating in the unnamed module space. Eventually, all applications can be broken down into JPMS modules, similar to the Java 9 runtime itself. When the entire stack is converted to JPMS modules, we can finally take full advantage of the strong encapsulation, reliable configuration mantra that Java 9 touts.
Since the beginning of Liberty, the entire runtime has been modular, thanks to OSGi. However, the current design of JPMS does not play will with OSGi, so getting these two different modular frameworks to co-exist will be difficult. Fortunately Thomas Watson, Senior Software Engineer at IBM, has been experimenting with ways to integrate OSGi and JPMS. He has also written a series of posts describing his experiments: Java Module Layers and OSGi Bundles and OSGi with Java Modules all the way down.
To summarize Watson’s findings, here are the main reasons why JPMS and OSGi do not currently work together:
- In JPMS, a module A can read module B if any of the following are true:
- Module A’s
module-infodeclares that it requires module B.
- A class from module A invokes
--add-reads A=BJVM argument is specified. Note this ought to be a temporary solution.
For OSGi to work with JPMS, the OSGi bundle loader must be able to grant read access to modules at runtime, which can only be accomplished with option (b). However, the OSGi bundle loaders are part of the OSGi framework, not a part of each module they are loading. For this reason, option (b) is not feasible in its current state.
- Module A’s
- Several types of frameworks (e.g. dependency injection, JPA, and serialization) must be able to instantiate and inject non-exported types. For this to work with JPMS, every module would have to export all of its internal packages for the sake of these frameworks. This would prevent Java applications that use any of these frameworks from practicing strong encapsulation. If this issue remains unresolved, it would likely result in an undesirable paradigm where Java EE and OSGi developers export all packages.
- JPMS modules must be statically resolved within a layer. OSGi, on the other hand, allows all bundles (i.e. modules) to be dynamically loaded and unloaded.
The above items indicate that the current design of JPMS is contrary to its design requirements, which state:
“It must be possible for another module system, such as OSGi, to locate Java modules and resolve them using its own resolver, except possibly for core system modules.”
Fortunately, there are a set of proposals in place that, if implemented, will allow JPMS and OSGi to be compatible:
- If a framework (e.g. OSGi) that manages layers has authority to establish read access on modules with its layers, OSGi dependencies will be independent of JPMS dependencies. This allows cycles within OSGi bundle dependencies and avoids cycles in JPMS module dependencies. This proposal is represented by #ReadabilityAddedByLayerCreator.
- To resolve this issue a new
module-infodirective will likely be needed which has a separate meaning from the
exportsgrants standard compile time access to any types in the specified package. A new directive could be introduced that makes the specified packages accessible via the core reflection API. The current proposal suggests the
opensdirective, which allows the specified package to be accessed at runtime by the core reflection API, but not at compile time. From a Java EE and OSGi perspective it would be best if all packages were open by default, which has been proposed by Neil Bartlett, a member of the JPMS Expert Group. The open module proposal is represented by #ReflectiveAccessToNonExportedTypes.
- Although JPMS modules cannot be dynamically resolved within a layer, the layers themselves can be dynamically resolved. Watson points out that layers could be used to represent static sets of resolved JPMS modules and OSGi bundles. If Layer A contains a set of bundles and a new bundle needs to be loaded, a new layer, Layer B, can be established with the new bundle plus the same set of bundles as Layer A. Implementing this with the current design is not feasible because layers must be hierarchical, thus causing an ever-expanding chain of layers that would stay in memory forever. However, if layers could have more than one parent layer, there would be no need for layer chaining, thus allowing unused layers to be garbage-collected. This proposal is represented by #NonHierarchicalLayers.
The original design of JPMS does not interact well with Java EE and OSGi. Fortunately, the JPMS Expert Group has been receptive to issues raised by the community, such as those listed above. It is important that the JPMS design continues to evolve in a way that supports compatibility with Java EE and OSGi. If it does not, Java 9 will provide a modularity system that will only be consumable by Java users who don’t currently care about modularity.
Although the aforementioned issues are resolved with their current proposals, there will still need to be significant work done to update the OSGi runtime for JPMS. In the meantime, OSGi-based programs such as Liberty will have to operate in the unnamed module space.
It is clear that the adoption of Java 9 will be slower than any previous Java version, due to the the scale of the changes being made. We are pleased to see that there are workarounds in place to get up and running quickly with Java 9, but completely embracing Java 9 and JPMS will be a long journey. Although it may seem like JPMS is re-inventing the wheel when it comes to modularity, the redundancy makes sense given that OSGi cannot be used to modularize the JDK itself.
From a Liberty perspective, it is unlikely that the Liberty runtime will ever be changed to use pure JPMS modules, since Liberty is built from the ground up with OSGi. Instead, we will be looking for ways to make our our OSGi runtime coexist with the JPMS. This coexistence will be essential if we are to support user applications that intend to take full advantage of Java 9.