dagblog.
← Archive

Java Module System


Improvements are planned for application deployment in Java 7, covering modularity, versioning and management. That’s the good news. The less welcome news is that the solution doesn’t look compatible with OSGi — The Dynamic Module System for Java, the specification behind products in the Eclipse tool family, for example. Everything points to another “war” brewing.

The Java Module System is covered by JSR-277, together with JSR 294, which standardizes what are called superpackages. If both specifications reach their final form, they should become part of Java 7.

Java deployment is tied to JAR packages, a concept from the platform’s early days. At the packaging level, JARs provide modularity for applications and components, setting aside the special case of enterprise Java. Application A, packaged in a.jar, might depend on component B, packaged in b.jar. There’s no standard way to express the following to the JVM:

  • A depends on B.
  • B must be at version X.Y.Z.
  • Does B depend on another component?
  • If B isn’t available on the classpath, where can it be downloaded?

This is why JSR-277 defines four key components.

  • A module
  • A versioning scheme
  • A module repository
  • JVM-level support for validation, lookup and loading

Modules

A module is the smallest unit of granularity. It contains classes, metadata and possibly other resources, such as images and properties files. The metadata defines the module’s name and version, its dependencies on other modules, class visibility through superpackages, and the complete set of classes and resources it contains.

The metadata lives in a file called METADATA.module, which might look like this.

@Version(“1.0.0”)
super package com.sun.cool.library { // module name
// exported class(es)
export com.sun.cool.library.ClassA;
export com.sun.cool.library.InterfaceB;
// module membership
com.sun.cool.library.ClassA;
com.sun.cool.library.InterfaceB;
com.sun.cool.library.PrivateClassC;
com.sun.cool.library.abc.moreStuff;
// no import
}

Superpackage

Exported classes overlap with the superpackage concept defined in JSR 294. A superpackage gives you finer-grained control over class visibility across modules at the package level.

Suppose we have classes in the package org.framework.api and classes in org.framework.api.internal. To make classes from the internal package visible in org.framework.api, they have to be declared public. That makes them globally visible, though.

By defining a superpackage inside the module I can make only the classes in org.framework.api globally visible exports. Anyone can import and use them. If a class outside the superpackage tried to import a class from org.framework.api.internal, it would get the same result as any class that tries to import a package-private class today.

Superpackages can also be defined outside modules, directly in JAR files. For now, the two specifications, 277 and 294, define different formats for exporting classes. It’s unclear which one will be chosen in the end. The relationship between the specifications looks fairly loose to me. I’d recommend Andreas Sterbenz’s article Superpackages in JSR 294, which has a good Q&A.

Back to modules. The whole module is packaged in a file named according to the convention <name>-<version>[-<platform>-<arch>].jam. Incidentally, the JAM extension stands for Java Module, so we’ll be jamming. Here’s an example of the file structure.

org.foo.complex-1.0.0.jam:
/META-INF/MANIFEST.MF
/MODULE-INF
METADATA.module
bin/windows/x86/bar.dll
bin/linux/x86/bar.so
bin/solaris/x86/bar.so
bin/solaris/sparc/bar.so
lib/javahelp-1.2.0.jar
/org/foo/complex/ComplexApp.class
/icons/drawing.jpg

Java itself should also be versioned in modules like these. For example, we’ll have a Standard Edition module exporting all classes from the packages java.* and javax.*. A module that depends on Java 7 or later will then be able to declare that dependency in its metadata: //
ImportModule(java.se, @VersionConstraint("1.7+"))

Versioning scheme

The versioning scheme (major[.minor[.micro[.update]]][-qualifier]) describes a module’s version. The system treats a module with the same name and a different revision as a different module. Multiple versions can coexist in one JVM. Another interesting feature is a convention for upgrading versions according to API changes, which the JVM won’t enforce. For example, incrementing the micro version means backward compatibility hasn’t changed.

Module repositories

A module repository lets you store, search for and retrieve modules. Repositories can form a tree, with delegation to a parent. A repository can live on the network. That makes it possible to have one centrally managed repository that all Java applications in an organization use.

A repository can do things such as verify bytecode and check digital signatures. It lets the JVM load individual modules only when they’re needed. The repository should be defined abstractly so specialized implementations can be built, for example using a database.

JVM-level support

The JVM will, of course, use all the capabilities of the Java Module System. It will load the modules it needs from the repository, and class loading will be adapted to let different revisions of a module coexist. The JVM will also check access to superpackages and make sure all dependencies are available.

Conclusion

I mentioned compatibility, or rather the lack of it, with OSGi at the start. If you’re interested in the comparison, read the JSR 277 review on the OSGi site. It’s interesting reading. As the review points out, JSR 277 leaves out dynamic management of modules at runtime: deploy, undeploy and redeploy.

Note: JSR 277 is still an early draft. Everything in this article refers to that version of the specification. Some details may no longer apply in the future.

Sources

Medium version · Archived original page

Related articles

Related CZ Podcast episodes