Whenever a new version of Java is released, there’s a lot of excited discussion about new language
constructs and APIs, features, and benefits. But the excitement quickly wanes for legacy developers when they remember that they must maintain and enhance existing applications, rather than create new ones. Most applications must be backward compatible with earlier versions of Java, which do not support new features presented by shiny new Java releases. So, legacy developers resign themselves to sit on the sidelines and watch.
Fortunately, Java 9’s designers have kept this in mind, and have worked out ways to make Java 9’s new features accessible for developers who have to worry about their applications supporting older versions of Java. Here we will discuss how new features in Java 9 — multi-release JAR files, Project Jigsaw (the new module system), and the modular JDK and jlink — make Java 9 usable and relevant to legacy Java developers.
Multi-Release JAR Files
Until recently, there hasn’t been a good way to use the latest Java features while still allowing the application to run on earlier versions of Java that don’t support that application. Java 9 finally provides a way to do this for both new APIs and for new Java language constructs: multi-release
Multi-release JAR files look just like old-fashioned JAR files, with one crucial addition: there’s a new nook in the JAR file where you can put classes that use the latest Java 9 features. If you’re running Java 9, the JVM recognizes this nook, uses the classes in that nook, and ignores any
classes of the same name in the regular part of the JAR file. If you’re running Java 8 or earlier, however, the JVM doesn’t know about this special nook and will ignore it, and only run the classes in the regular part of the JAR file. In the future, when Java 10 comes out, there’ll be another
nook specifically for classes using new Java 10 features, and so forth.
The Java 9 JDK will contain a version of the jar.exe tool that supports creating multi-release JAR files. Other non-JDK tools will also provide support.
The Java 9 module system (also known as Project Jigsaw), is undoubtedly the biggest change to Java 9. One goal of modularization is to strengthen Java’s encapsulation mechanism so that the developer can specify which APIs are exposed to other components and count on the JVM to enforce the encapsulation. Modularization’s encapsulation is stronger than that provided by the
public/protected/private access modifiers of classes and class members. The second goal of modularization is to specify which modules are required by which other modules, and to ensure that all necessary modules are present before the application executes.
In this sense, modules are stronger than the traditional classpath mechanism, since classpaths are not checked ahead of time, and errors due to missing classes only occur when the classes are actually needed, which means that an incorrect classpath might be discovered only after an application has been run for a long time, or after it has been run many times.
Java 9 offers both a classpath and a module path. The classpath works just as before, and you can keep using it. JAR files in the module path are interpreted as modules — they expose APIs and have dependencies that can be checked at compile time. If you want to go the extra mile, you can modularize a JAR file by adding information specifying which APIs are exposed and which other
modules are required. However, even if you don’t, if your JAR file is in the module path, it’s considered an automatic module, although it lacks some information that can be
used to check whether anything is missing. Also, all the JAR files in the classpath are considered part of the unnamed module, which means they become part of the module system, too.
This means that it really doesn’t matter whether your JAR files are modularized or whether they’re still old school. It may even be the case that you can’t modularize a JAR file because it doesn’t belong to you. All modules play in the module system, whether they’re up to date or not, and
whether they’re in the classpath or the module path. If you modularize your JAR files and put them in the module path, you’ll get additional benefits and avoid potential errors. But even if you don’t, you get many of the benefits of the module system. It all might sound complicated, but
it just works.
How to Supply Your Own Java Environment With Modular JDK and jlink
One problem with legacy Java applications is that the end user might not be using the right Java environment, and one way to guarantee that the Java application will run is to supply the Java environment with the application.
Java allows the creation of a private or redistributable JRE, which may be distributed with the program. The JDK/JRE installation comes with instructions on how to create a private JRE. Typically, you take the JRE file hierarchy that’s installed with the JDK, keep the required files, and retain those optional files whose functionality your application will need. The process is a bit of a hassle: You need to maintain the installation file hierarchy, you have to be careful that you don’t leave out any files and directories that you might need, and, while it does no harm to do so,
you don’t want to leave in anything that you don’t need, since it will take up unnecessary space. It’s easy to make a mistake. So why not let the JDK do the job for you?
With Java 9, it’s now possible to create a self-contained environment with your application and anything it needs to run. No need to worry that the wrong Java environment is on the user’s machine, and no need to worry that you’ve created the private JRE incorrectly.
The key to creating these self-contained runtime images is the module system. Not only can you modularize your own code (or not), but the Java 9 JDK is itself now modularized. The Java class library is now a collection of modules, as are the tools of the JDK itself. The module system requires you to specify the base class modules that your code requires, and that in turn will specify the parts of the JDK that are needed. To put it all together, we use a new Java 9 tool called jlink. When you run jlink, you’ll get a file hierarchy with exactly what you’ll need to run your application — no more and no less. It’ll be much smaller than the standard JRE.
With jlink, it becomes easy to package up your application and everything it needs to run, without worrying about getting it wrong, and only packaging that part of the runtime that’s necessary to run your application. This way, your legacy Java application has an environment on which it’s guaranteed to run.
With this newfound knowledge, it’s clear that legacy developers need not sit on the sidelines and watch as everyone else gets to play with the new Java 9 features. Using these approaches, anyone can take advantage of all Java 9 has to offer, without breaking compatibility with earlier versions of Java.