JPMS Modulepath

I originally focused on the internal parts of JPMS and not so much its interaction with the outside world. This left the module path out of focus and I, admittedly, had the wrong impression. After my last article it became clear that this rather important part of the specification was neither in the JLS draft nor in the JPMS design document but in the Javadoc of the java.lang.module package. That was not the only mistake I made, I also started with the compiler view. Since then I realised that the compiler view has an oddity. During compilation, the module compilation unit can access annotation types from other modules. However, at that time you do not know yet what other modules are observable. Just like the complete separate grammar for a Java source file and the module definition it is an another indication that class files are not really suitable. It therefore seems simpler to focus on the runtime first.

The following class diagram provides an overview of the JPMS design as I understand it.

image

The Module and ModuleLayer class are the runtime artefacts. The Configuration is a resolution of a set of root module names using two Module Finders and parent configurations. The configuration is then used to create a Module Layer with modules.

A Configuration takes a number of roots, a parent configuration, and two module finders. It locates the modules by name, from (in order):

for each _before_ Module Finder
  for each Directory in Module Finder
    names map = empty
    for each file in Directory
      create module descriptor
      if the name is in the map, fail
        else add name, descriptor to the map
	
    if the map contains the searched module, 
      return the Module Descriptor

for each parent in order
  search the parent for the requested module recursively
  if found, return the found Module Descriptor

for each _after_ Module Finder
  for each Directory in Module Finder
    names map = empty
    for each file in Directory
      create module descriptor
      if the name is in the map, fail
        else add name, descriptor to the map
	
    if the map contains the searched module, 
      return the Module Descriptor

This clearly resembles the infamous but well known -classpath and other chain of responsibility patterns. They are so easy to use! The developer can tweak the resolution by adding directories ahead of the chain and in that way overriding modules later in the chain.

However, there are quite a number constraints on the Configuration:

That is quite a number of constraint to leave up to such a fragile search strategy. In practice, it is really hard to see that anything but a simple ‘Hello World’ program requires strict curation of the module path. It also seems extremely unlikely that you can share directories on your module path, after all, what you then have is DLL Hell. An update of one application can destroy another application. The inevtiable consequence is that each application will require its own container with a set of strictly curated members. (e.g. a directory with a set of JARs.)

Realise that the core problem is the lack of versions in the module. A module name, unless it embodies a version or GUID, is not a unique identifier for a module. This makes it impossible to ‘find’ a module in a repository, there is just not enough information in the query to the Module Finder.

I therefore see no way around tooling that for a given application provides a directory (or some container) with the JARs that are part of that application? Clearly, this is not optimal because it will create a huge redundancy of directories with many will have the same JARs. Yes, you can be clever with file links and surely people will get this to work. But one wonders if there is not a much simpler solution.

Peter Kriens


Comments