What Is an Automatic Module

What is an automatic module?

I first answer your actual question ("What is an automatic module?"), but I also explain what they are there for. It is hard to understand why automatic modules behave the way they do without that information.

What is an automatic module?

The module system creates a module from every JAR it finds on the module path. For modular JARs (i.e. those with module descriptors) that is straightforward as they define the module's properties (name, requires, exports). For plain JARs (no module descriptor) this approach doesn't work, so what should the module system do instead? It automatically creates a module - an automatic module, so to speak - and takes the most safe guesses for the three properties.

Name

Deriving the name is a two-step process:

  • if the JAR defines the Automatic-Module-Name header in its manifest, it defines the module's name
  • otherwise, the JAR file name is used to determine the name

The second approach is intrinsically unstable, so no modules with a dependency on such an automatic module should be published. Maven warns about that.

Requires

Since a plain JAR expresses no requires clauses, the module system lets automatic modules read all other modules that make it into the readability graph (aka module graph). Unlike explicit modules, automatic ones also read the unnamed module, which contains everything that was loaded from the class path. This seemingly minor detail turns out to be very important (see below).

Automatic modules have some further readability quirks, though:

  • As soon as the first automatic module is resolved, so are all others. That means, once a single plain JAR on the module path is referenced by another module, all plain JARs are loaded as automatic modules.
  • Automatic modules imply readability on all other automatic modules, which means a module reading one of them, reads all of them.

Taken together, this can have the unfortunate effect that an explicit module (i.e. a non-automatic one) that depends on several plain JARs can get away with only requiring one of them (as long as the others end up on the module path as well).

Exports/Opens

Since the JAR contains no information which packages are considered public APIs and which aren't, the module system exports all packages and also opens them for deep reflection.

More

The module system also scans META-INF/services and makes the automatic module provide the services named therein. An automatic module is assumed allowed to use all services.

Finally, the Main-Class manifest entry is processed as well, so a plain JAR that defines one can be launched just like an automatic module where the main class was set with the jar tool (i.e. java --module-path my-app.jar --module my.app).

Proper module

Once the automatic module was created, it is treated as any other module. This explicitly includes that the module system checks it as any other module, for example for split packages.

What is an automatic module for?

One of the reasons for introducing modules was to make compiling and launching applications more reliable and find errors sooner that was possible with the class path. A critical aspect of that are requires clauses.

To keep them reliable, there is no way for a module declaration to require anything other than a named module, which excludes everything loaded from the class path. If the story ended here, a modular JAR could only depend on other modular JARs, which would force the ecosystem to modularize from the bottom up.

That is unacceptable, though, so automatic modules were introduced as a means for modular JARs to depend on non-modular ones and all you need to do for that is place the plain JAR on the module path and require it by the name the module system gives it.

The interesting bit is that because automatic modules read the unnamed module, it is feasible (and I generally recommend doing that) to leave its dependencies on the class path. This way, automatic modules act as a bridge from the module to the class path.

Your modules can sit on one side, require their direct dependencies as automatic modules, and indirect dependencies can remain on the other side.
Every time one of your dependencies turns into an explicit module, it leaves the bridge on the modular side and draws its direct dependencies as automatic modules onto the bridge.

What does Required filename-based automodules detected. warning mean?

Automatic module recap

An explicit module (i.e. one with a module-info.java) can only access code of modules that it requires (ignoring implied readability for a moment). That's great if all dependencies are modularized, but what if they are not? How to refer to a JAR that isn't modular?

Automatic modules are the answer: Any JAR that ends up on the module path gets turned into a module. If a JAR contains no module declaration, the module system creates an automatic module with the following properties:

  • inferred name (this is the important bit here)
  • reads all other modules
  • exports all packages

Maven relies on that mechanism and once you create a module-info.jar it places all dependencies on the module path.

Automatic names

There are two ways to infer an automatic module's name:

  • entry in the manifest
  • guess from the JAR file name

In the first case, the name was deliberately picked by the maintainer, so it can be assumed to be stable (for example it doesn't change when the project gets modularized). The second one is obviously unstable across the ecosystem - not all project setups lead to the exact same file names of their dependencies.

What does it mean?

The reason for the warnings is that some of your dependencies are automatic modules and do not define their future module name in the manifest. Instead, their name is derived from the file name, which makes them unstable.

Stable names

So why are unstable names such a problem? Assume your library gets published with requires guava and my framework gets published with requires com.google.guava. Now somebody uses your library with my framework and suddenly they need the modules guava and com.google.guava on their module path. There is no painless solution to that problem, so it needs to be prevented!

How? For example by discouraging developers from publishing artifacts that depend on filename-based automatic modules. /p>

Java 9, Jigsaw and automatic modules

You need to add a library entry first, to make it available under Modules:

Step 1: Add a library (Add -> Java -> jar file)

Sample Image

Step 2: Select the module (remember to click "Apply")

Sample Image

After that, the module-info.java file will be successfully validated:

Sample Image

Why Java Automatic modules do not contain exports section

I believe, the command line option --describe-module within the jar tool is just to describe what the jar file is about, if its an explicit module the complete descriptor is shared whie for automatic module just the name is depicted.

Quoting from the command jar --help

Print the module descriptor, or automatic module name


On the other hand, if you try using jdeps for generating module-info.java of an automatic module given a jar file, you can notice that such packages exports are actually present in the module declaration. Say for example:

jdeps -verbose:class --generate-module-info ../Desktop  ~/.m2/repository/org/apache/commons/commons-lang3/3.8.1/commons-lang3-3.8.1.jar

writes to .../Desktop/org.apache.commons.lang3/module-info.java the following

module org.apache.commons.lang3 {
requires transitive java.desktop;

exports org.apache.commons.lang3;
exports org.apache.commons.lang3.arch;
exports org.apache.commons.lang3.builder;
exports org.apache.commons.lang3.concurrent;
exports org.apache.commons.lang3.event;
exports org.apache.commons.lang3.exception;
exports org.apache.commons.lang3.math;
exports org.apache.commons.lang3.mutable;
exports org.apache.commons.lang3.reflect;
exports org.apache.commons.lang3.text;
exports org.apache.commons.lang3.text.translate;
exports org.apache.commons.lang3.time;
exports org.apache.commons.lang3.tuple;
}

Automatic Module cannot be found

Change

requires json;

to

requires org.json;

The reason for that is specified in the META-INF of the artifact.

Automatic-Module-Name: org.json

What does a module.a binds module.b mean during the resolution?

This most likely refers to the bind operation of resolveAndBind processing usesprovides relationships.

This method works exactly as specified by resolve except that the graph of resolved modules is augmented with modules induced by the service-use dependence relation.

More specifically, the root modules are resolved as if by calling resolve. The resolved modules, and all modules in the parent configurations, with service dependences are then examined. All modules found by the given module finders that provide an implementation of one or more of the service types are added to the module graph and then resolved as if by calling the resolve method. Adding modules to the module graph may introduce new service-use dependences and so the process works iteratively until no more modules are added.

So, binding takes place after resolving, but still before any code of the involved modules has been invoked. Namely, it doesn’t matter whether the ServiceLoader is actually used within the module(s) or not. But when it is used, it will utilize the already available information. So a lot of potential problems have been precluded at this point already. This is also the reason why we could build optimized module images pre-linking such services.

This, however, doesn’t work with automatic modules, as they don’t have uses directives. Without that information, service lookups can only be done when an actual use of ServiceLoader happens, just like with classes loaded through the old classpath.


Note that the issue in the linked Q&A is a bit different. According to the OP’s information, a module declaration has been used when compiling. But then, the OP ran the application using the -jar option which puts the specified jar file on the classpath and loads it from there, creating an unnamed module, rather than an automatic module. This ignores the compiled module-info and in absence of old-fashioned META-INF/services/… resources, no service implementation was found at all. The OP’s code was designed to fall back to the default service implementation if none had been found, which is indistinguishable from the scenario of finding the default service through the ServiceLoader.

The crucial differences of your answer are the start method -m base.service/base.service.ServiceUser, which will locate the base.service through the module path and launch its base.service.ServiceUser, as well as --add-modules …user.service, to ensure that the module providing the service will be added to the runtime environment. The latter is needed since there is no direct dependency from the launched module base.service to the user.service, so it wouldn’t be loaded automatically.

So in your answer’s setup, both modules are loaded as such. When they have a module-info, its provides directive will be processed to find the service implementation, otherwise, it’s an automatic module and must have a META-INF/services/… file. As said above, there is no equivalent to the uses directive for automatic modules, so you will never see a “binds” log entry for them. But they may still use the service lookup.

How to use 3rd party library in Java9 module?

You can use your library as an automatic module. An automatic module is a module that doesn't have a module descriptor (i.e. module-info.class).

But what name do you need to specify to refer to an automatic module? The name of the automatic module is derived from the JAR name (unless this JAR contains an Automatic-Module-Name attribute). The full rule is quite long (see Javadoc for ModuleFinder.of), so for simplicity, you just have to drop the version from its name and then replace all non-alphanumeric characters with dots (.).

For example, if you want to use foo-bar-1.2.3-SNAPSHOT.jar, you need to add the following line to module-info.java:

module <name> {
requires foo.bar;
}


Related Topics



Leave a reply



Submit