This is the second edition of the series. For introductory edition, click here.
In this chapter, we are going to see the modules. In the previous chapter, we’ve seen all the advantages of modularity. We’ve learned about the concepts in the abstracts, but now it’s time to see the modules.
The Modular JDK
I want to introduce you to the very first application that has been modularized using the Java Module system ever, which is the Modular JDK itself. The Java Development Toolkit is the prime example of how things can be modularized, so we will have an in-depth look at what the current state of modular JDK is and how it differs from the original JDK we know so far.
Above is a depiction of occurrence of pre-java 9 JDK. You may not be aware of it but when you run a java application, you run it on the top of the runtime library itself. Its runtime library is packaged in a file called rt.jar, and it is one huge library containing all possible classes from the java platform that your application may use. This ranges from collection classes that everybody uses to string, object, the basic type to much more esoteric types, like corba classes or RMI classes. Many applications don’t use that classes anymore, but they are still part of one big rt.jar. This one big rt.jar is above 20 years old. You can probably see how this has grown into a big and tangled bowl of classes. It’s not very clear, what depends on what, inside this jar. Having so many entangled classes in one huge library makes it very hard to evolve. More than that, it is very hard to remove and deprecate code from this big jar. Summary of this is that the situation for the JDK libraries is pretty dire and it wasn’t about to get better.
Fortunately, the modularization of JDK offers a way forward.
The picture painted of the JDK’s library may remind you of your own application. It may be a big monolith and have unknown dependencies as well, where changing one part will affect many other unknown parts, which makes it hard to evolve or change. If so, you’ll probably appreciate what you’ll see in the next picture because a modularized JDK shows you how you can modularize your own applications.
What we see above is a diagram of the modular JDK in Java 9. It contains only a few modules. The full modular JDK has over 90 so-called platform modules. Now, this diagram is of a so-called dependency graph. Every box indicates a module and a box can have an arrow to another module, which means it needs the other module to perform its functions. The most important Module here is java.base at the bottom of the diagram. It is the module that every other module always depends on. It contains foundational classes like java.lang.object, java.lang.integer and so on. Every module implicitly depends on java.base.
However, we can make the dependency as explicit as is done above. But this makes the diagram quite messy, so typically we omit the dependencies from modules to java.base because it’s always there.
Now as you can see, the java.logging and java.xml modules still have an explicit arrow in the picture. We do that whenever java.base is the only dependency of a module.
Let’s look at the modules that does have dependencies besides java.base.
For example, the java.sql module uses the java.logging module and it makes sense because it may have some internal logging to do and it uses the java.xml modules which may be a bit more surprising, but it’s there to handle the xml capabilities of databases. By looking at the modules and their explicit dependencies in the above diagram you already know a lot more about what is in a JDK than you would when looking at the rt.jar like before. Here we have a clear picture of what kind of functionality is in a JDK and what are the dependencies between the different modules offering this functionality. I hope you get as excited about this as I am, because having this information explicitly encoded into modules with all the dependencies will make it much easier to create reliable applications and if we apply the same principles to our own applications, they will become as understandable as the above picture as well.
Having an explicit dependency is one of the three tenets of modularity, but we also need encapsulation and well-defined interfaces. So, let’s zoom in on a single module and see how the Java module system handles these concerns. In the above picture, we are looking at the single module java.base and as you can see, it has two sections. The upper half lists some packages. In this case java.lang, java.util, java.io but there is many more in the actual java.base. These packages are all part of the public interfaces of this module. That means every module depending on java.base will be able to see everything in the java.lang, java.util and other packages. Below the line is the shielded part of the module, we see different sources of packages. The names of the packages already signal that those are internal implementation details. In this case, the other module that depends on java.base will not be able to access anything in those packages. This is the strong encapsulation that the module system offers. If you really must access those classes, there are some workarounds (will discuss that later), but being a good modular citizen, the idea is of course that you only use public parts of the module and you are never supposed to know the internal details.
By now, you have a pretty good understanding of modules and modular JDK, but you’re probably wondering how these modules are defined? Well, I am glad you asked because that’s what we’re going to look at next. For next edition, watch this space.
Senior Software Engineer
Originally published at medium.com on June 29, 2018.
Originally published at medium.com on June 29, 2018.
Also published on Medium.