Java 9 has been out for a little while now, and hopefully you already had some time to learn about the module system. The Java 9 Modularity book that I co-authored with Sander Mak is obviously a great start!
In the past weeks we have seen several frameworks and libraries release early support for the module system. In many cases the support means “automatic module compatible”. The framework should be useable as an automatic module, but is not modularized itself yet. One of my favorite go-to frameworks is Vert.x, and the 3.5 release should make Vert.x usable from modules. Time to put it to test!
Looking at the Vert.x project, it is clearly designed with modularity in mind, although not specifically designed for the module system. Every feature in Vert.x comes in a (usually) small module. Do you need to write REST services? Vertx-web got you covered. Need a HTTP client: vertx-web-client. Mongo? Vertx-mongo. This makes Vert.x usually very light weight, because you only pull in what you need. This makes the project also a perfect candidate for modules down the road. I decided to build a web-based variation of the EasyText app that we use for many of the examples in the book. You invoke the service over HTTP with a “topic” as request parameter, and the service will find the Wikipedia page for that topic and analyze it’s text complexity. This isn’t a particularly useful application, but it’s just complex enough to demo a modular design. The code can be found here.
The web server that takes user requests is implemented in the
easytext.web module, which uses
vertx.core as an automatic module. When a request comes in, it invokes the
easytext.pagefetch module to get the Wikipedia text for the “topic” passed in as a request parameter by the user. The
easytext.pagefetch module only exports it’s API, while the actual implementation for page fetching is provided as a service.
The page fetcher uses
vertx-web-client for the actual HTTP request to Wikipedia. Note that
easytext.web doesn’t know or care how easytext.pagefetch is implemented, it only know about it’s API.
The actual text complexity calculation is done by two different algorithms: Flesh Kincaid and Coleman. Both implementations are provided as services, so that there is no coupling between
easytext.web and the analyzers. It’s also possible to add new implementations of analyzers without changing any code, just add them to the module path. The same analyzer modules are used in the EasyText GUI and CLI apps, which shows a good case of re-usability by using modules and services.
The project is a multi module Gradle project. Each module is a sub project. This way we can build all modules together with a single command from the root, but we do have clear module boundaries just looking at the directory structure.
The build uses the still experimental Gradle Jigsaw plugin. This plugins sets the –module-path and other related arguments for the compiler, so that we can write our code with modules. A good example to look at is the build file for the
This module is our “main” module, and has dependencies to other modules in the project.
If we were to use Maven, we wouldn’t have to rely on an experimental plugin, Maven already has excellent support for Modules. The project relies on two different Vert.x modules:
- Vert.x Core
- Vert.x Web Client
The Vert.x dependencies, and their transitive dependencies, are used as automatic modules. This is mostly transparent, Gradle/Maven takes care of setting up the module path.
Providing a Vert.x instance
Whenever you use any Vert.x functionality, you typically need to have an instance of the Vertx interface to do so. For example, when using the web client, the client is created by passing in the Vertx instance. Vert.x has the concept of Verticles that give you access to the Vert.x instance, but this is not a requirement. I chose to have one module (
easytext.vertx) create the instance, and provide it to other modules as a service. This is completely optional, but another nice service use case.
Vert.x worked as automatic modules out of the box. However, while experimenting more, I did run into a problem. When using both
vertx-web-client, there’s a split package. A split package means that the same package exists in two different modules. The module system does not allow this and will refuse to start. Split packages are always a bad idea, and definitely a code smell when it comes to modularity. In this case, the split package was caused by a package-info.java, the package doesn’t even contain any code, so the problem is very limited.
As a user of a library you’re kind of stuck when this happens though. The only workaround is to patch the code yourself, but that might be difficult.
The vert.x team will continue to work to straighten out these kinks, but in some cases this means breaking backwards compatibility. This means we will have to wait for the next major release for this to happen.
So, is it ready?
The split package problem, and the fact that we rely on an experimental Gradle plugin, show that the ecosystem is still in an early state. You should expect some early adopter type problems when jumping on modules today (while using automations modules). The experiment also shows that everything just mostly works, and when it does, it’s very easy to use. Services also prove to be a very valuable tool for decoupling. Although dependency injection can still work really well, services are a great alternative with better alignment with the module system. Using modules will just become even easier in the future when library and framework support gets better. Start experimenting today and report any issues you run into to move the ecosystem forward!
The code for this example can be found here.