Finally: Java 9
Land Ho!
For a long time, Java [1] has easily topped the rankings of popular programming languages. This is not surprising; after all, programs developed in Java can be used anywhere from cellphones to mainframes and across all operating systems. However, a glimpse at the former shooting stars Perl and PHP shows that there is no guarantee of permanently riding atop the heap.
Regular updates help keep the language at the cutting edge and the estimated nine million Java programmers happy, but they have needed patience, because major release 9 has taken its time. Originally, it was planned as an interim release to deliver to the themes that were not ready in time for Java 8.1. "As quickly as possible" originally meant mid-2015, then mid-2016, and finally 2017. It remains to be seen whether the project schedule will be followed this time, although test builds are now available online [2].
Jigsawing
One core feature of Java 9 par excellence is the Java Platform Module System that became known as Project Jigsaw [3]. Thus far, the Java compiler and the run-time environment have thrown all available libraries from the class path into a large pot. Whether this turns out to be a tasty soup or smoke and mirrors partly depends on subtle changes in the class path.
Many developer hours have been spent in class path hell battling against the class loader. The problem is, only the right libraries and library versions in the appropriate order result in a working application. Approaches such as Maven, ServiceLoader
or OSGI only partially mitigated the situation and had to take a wide detour around the standard libraries.
With Jigsaw, this should be different. The new modular system pervades the entire stack of the compiler, from the Java Runtime Environment (JRE) and the standard libraries to the application libraries. The development effort for Jigsaw was originally estimated to be about size XL, but in the real world, a number of Xs were added. The initial plans had their sights set on Java 7.1; ultimately, Jigsaw now first appears in Java 9. All three affected versions were delayed by years.
Totally Modular
The result is not only improved handling of libraries, but modularization also allows developers to tailor the JRE to the needs of an application. After all, the standard libraries in Java 8 weigh in at around 60MB and 20,000 classes. They not only need space on the hard drive, but the computer also needs to load them into memory at every startup and check the classes and methods they contain. Less powerful machines (industrial automation, IoT, network appliances) welcome every single megabyte saved, so the application uses less space and launches faster.
Java 9 dismantles the monolithic standard library modules. If you do not need features such as the CORBA stack, the JavaScript interpreter, or encrypted XML documents, you will soon have the opportunity to tailor the installation to your own needs for the first time.
A Scenario
The scenario shown in Figure 1 is used as an illustration of the new modular strategy. The sample application consists of two proprietary modules (modulea
and app
). Also on board are two modules from Java itself and another two for logging.
The modulea
module includes everything related to the Customer
business object. It contains a Customer
interface, the matching CustomerService
for searching and saving, and the associated implementation (Impl
) classes. For this purpose, the software needs classes from the modules java.core
, java.xml
, and slf4j.core
. The app
module contains the application itself and requires modulea
plus logback.core
; the modules required in moda
are implicitly available.
At the file level, modules are ordinary Java archives in JAR format that additionally contain a description of the module. They each provide module_info.java
files that the developer compiles with the other classes and bundles into the JAR.
Listing 1 shows the module description for modulea
. The declaration starts with the module
keyword; the requires
point to the code to the necessary modules. The compiler and the JRE use this to load the modules or to inform the user that they are missing immediately on startup. For modules like app
to be able to use the two interfaces, the module description releases the contained package using export
.
Listing 1: modulea description
01 module de.lm.java9.modulea { 02 // required 03 requires java.base; 04 requires java.xml; 05 requires slf4j.api; 06 07 // exported 08 exports de.lm.java9.modulea; 09 10 // provides a service implementation 11 provides de.lm.java9.modulea.CustomerService with de.lm.java9.modulea.intern.CustomerServiceImpl; 12 }
All other classes from modulea
are not visible outside of the module. Although the CustomerImpl
class is declared as public
, the contained package cannot be reached outside of the module because of a missing export
. In this way, developers could enforce loose coupling so that the user of a library sees its interfaces but has no access to the actual implementation or helper classes.
Ultimately, access to the object instances of the exported interfaces are needed somehow. Putting the CustomerService
implementation in the public package would be contrary to the idea of loose coupling, because a user of the objects should not have to worry about how this is implemented. Here, the well-known but little-used ServiceLoader
enters the scene as a solution. It can be used to query a CustomerService
instance without knowing or accessing its implementation. Its application is shown in Listing 2, in which the main application queries CustomerService
without knowing anything about the related implementation class.
Listing 2: ServiceLoader application
01 // Create the ServiceLoader for a CustomerService 02 ServiceLoader<CustomerService> sl 03 = ServiceLoader.load(CustomerService.class); 04 05 // Use first implementation 06 CustomerService service = sl.iterator().next(); 07 08 set<Customer> customer = service.search("Tux*");
ServiceLoader
thus acts as a broker between different modules. To do so, the providing module must publish the implementation using the provides
keyword (Listing 1, line 11), and the module that is using it must publish it via uses
(Listing 3, line 7).
Listing 3: app description
01 module de.lm.java9.app { 02 // required 03 requires de.lm.java9.modulea; 04 requires logback.core; 05 requires logback.classic; 06 // uses Service 07 uses de.lm.java9.modulea.CustomerService; 08 }
Java 9 breaks down the normal run-time library into 96 small modules, such as java.core
and java.xml
. It is comparatively easy to switch your own applications to the new module concept; libraries used for this purpose do not usually need to support it immediately. Instead, it is typically fine to add the normal JARs to the module path (e.g., slf4j
or logback
). They are then available as modules in automatic mode.
Normal visibility rules from the class definition apply (private
, package
, public
). The module names each derive from the names of the JAR files. Through the use of these names, they can be used by real modules in named mode with a module description.
If you use dirty hacks like Class.forName
or load resources across JARs, you will not be able to get your application running on Java 9 without making changes. This means moving to the new strategy, or at least building a bridging module that combines the old and new worlds.
All told, the wait for Jigsaw seems to have been worthwhile. For example, the dependencies and published interfaces can now be cleanly defined and evaluated throughout the entire life cycle, from compiling through run time. Developers can force loose coupling for the first time using the ServiceLoader
mechanism and the different visibilities inside and outside a module.
The modular run-time environment itself allows for a smaller memory footprint on less powerful hardware. Class.forName
also causes comparatively little pain. The downside is the lack of support for module versions as offered by Maven. The only escape route here is to manipulate the module name as in modulea_v1
, modulea_v2
, and so on.
Tools
Java 9 offers two new tools for working with modules: The first takes care of tasks relating to module dependencies. Among other things, it prepares them as texts or graphs or generates module descriptions for existing JAR files. The run-time environment can be trimmed with JLinker to match a module description. A minimal installation thus shrinks from 275MB (Java 8) to 40MB, reducing the startup time of a simple application on a PC by two-thirds, or about 0.9 seconds.
Thanks to the new -html5
option, the Javadoc tool now generates HTML with a more modern, barrier-free design and appearance. Not only a matter of a modified stylesheet, the generated Java documentation impresses with, among other things, a built-in search for class and method names and tooltips (Figure 2). The displayed classes can be restricted to selected Java modules, or you can view only the modules for a particular class.
Shell Factory
JShell, a more recent addition, allows the use of uncompiled Java as an interpreted language. The idea is not new: Patrick Niemeyer presented a first implementation 17 years ago in the form of BeanShell [4]. However, it never supported the full range of Java and work ground to a stop over the past few years. This situation was reason enough for the Java compiler group to begin work on a new shell code-named Kulla (the Sumero-Babylonian brick god). As a REPL (read, evaluate, print, loop) tool, it allows Java code to be executed line by line and is launched by typing jshell
at the command line (Figure 3).
JShell evaluates the input after each newline, as in Python or TCL, whether it is normal Java code or one of the JShell commands. JShell commands all start with a slash; /help
shows an overview of the available commands. When a line is executed, Java 9 either automatically creates variables, or the developer has to define them with a type and name. The /var
command shows a list of defined variables and their values.
JShell accepts normal and static imports anywhere; /import
lists those defined so far. Developers can use JShell not only to test small snippets of code like the DateTimeFormatter
here, but it also supports the full syntax of Java 9, from a simple one-liner, through lambda expressions, to defining whole methods and classes.
Fortunately, programmers do not have to type flawlessly at the command line. If you mistype the code, a Java compiler error message appears, and you can retry. As in Bash, you can retrieve and edit previous input using the arrow keys. Expansion of class paths by pressing the Tab key works much the same way.
If you prefer working in a graphical editor, you can launch one by typing the /edit
command (Figure 4) or load externally written code from a file with /o
. Unlike compiled code, the semicolon at the end of the line is optional, and you don't have to worry about handling exceptions with try
and catch
blocks. More in-depth integration with the usual suspects (e.g., NetBeans, Eclipse, and IntelliJ) is expected in the next few months; IDEs for Python show what is possible in this direction.
Running
Work on the run-time library was also intended for Java 9, but there is not much of this left because of the delay. The most interesting new entry is the enhanced process API, which makes it possible, after years of waiting, not only to launch external processes, but also to stop them, access process IDs, and conveniently wait for them to end.
Swing was given some fine tuning. Instead of the obsolete GTK+ 2 library, the current GTK+ 3 library is now used, thus supporting settings for high-resolution displays.
The HTTP client has also seen the writing on the wall with HTTP/2 support. The frame-based protocol offers less overhead and lower latency and is now fully supported, thanks to the new HttpRequest
and HttpResponse
classes. The HttpRequest
class is used to build a request, and the Fluent API lets programmers conveniently specify all the required information. Listing 4 shows an example that presents the results with HttpResponse
.
Listing 4: HTTP/2 client
01 HttpResponse response = HttpRequest 02 .create(new URI("http://www.linux-magazine.com")) 03 .headers("User-Agent", "Java9") 04 .GET() 05 .response(); 06 07 LOG.info("status : " + response.statusCode()); 08 09 Map<String, List<String>> headers = response.headers().map(); 10 for (String key : headers.keySet()) { 11 LOG.info(key + " : " + headers.get(key)); 12 } 13 14 String body = response.body(asString()); 15 LOG.info("body : " + body);
The question of which elements the Java developers would discard is almost more important than what has been added. To ensure backward compatibility, they have so far not removed a single class, method, or variable. Meanwhile, at least 60 classes and 400 methods are tagged as @deprecated
; some of them have already been marked for removal in Java 1.1. After 20 years, Oracle finally has relented and announced that it will remove obsolete parts.
The most prominent victim is the Java applet, which breathes its last breath in Java 10. It provided good service, boosting the popularity of Java in the early years, but it is undesirable in the browser for security reasons, just like Flash or Silverlight. Ironically, the outgoing applet leaves the biggest gap in the area of creating certificates, encrypted data transfer, or signatures; HTML5 lacks equivalent capabilities.
The cleanup will take place in small steps; the first one has already been taken. The @deprecated
annotation now comes with two optional attributes forRemoval
and since
, which mark the classes that will disappear in the future.
Summing Up
In addition to the points mentioned here, a number of changes are probably only relevant for a few developers, such as a Linux/s390x port from SAP and the Red Hat-contributed Linux/AAarch64 port for the 64-bit ARM platform. Unicode version 7 is now included instead of 6.2, and Java 9 can load TIFF images, support SHA-3, and more.
On the technical side, Java 9 has thus met with a warm reception; the changes have been extensively matched with the JDK enhancement process. Even 10 years after Sun released the source for Java, the development model still works quite well; participation by other companies is evidence enough. The coming months will show how long it takes for the essential libraries to become available as modules. The benefits are obvious.
Highly configurable libraries in particular use solutions that will no longer run with project Jigsaw. One can only hope that the permanent delays come to an end when Jigsaw is finally released. According to the release planning from 2014, Java 10 is supposed to be with us in 2017. The first plans for it have been drawn up, including the Valhalla [5] project to improve the type system and add speed. The currently unknown release date will keep programmers on the edge of their seats.