I am trying to understand how JPMS works.
From here
The classpath is not completely gone yet. All JARs (modular or not)
and classes on the classpath will be contained in the Unnamed Module.
Similar to automatic modules, it exports all packages and reads all
other modules. But it does not have a name, obviously. For that
reason, it cannot be required and read by named application modules.
The unnamed module in turn can access all other modules.
Please, note ...on the classpath will be contained in the Unnamed Module. Module is singular.
From here
For compatibility, all code on the classpath is packaged up as a
special unnamed module, with no hidden packages and full access to the
whole JDK.
Again unnamed module. Module is singular.
Do I understand right that there is always only one unnamed module in JPMS? Does it mean that applications that were developed before Java9 and not updated for Java9 will be loaded as one unnamed module?
Do I understand right that there is always only one unnamed module in JPMS?
In short
Generally speaking, no. But let's put it this way: If you place some or even all JARs on the class path and your application does not create class loaders to load any additional content, then there is only one unnamed module you need to care about.
In more detail
Every ClassLoader has its own unnamed module that it uses to represent classes that it loaded from the class path. This is necessary because the module system requires everything to be in a module.
As nullpointer's answer explains in detail, an application will by default use three separate class loaders. It is possible that it might spin up its own class loaders, for example to load plugins. If it doesn't do that, though, all application code will end up in the system/application class loader and hence in the same unnamed module. That's why there is typically only one you need to care about.
Does it mean that applications that were developed before Java9 and not updated for Java9 will be loaded as one unnamed module?
This has nothing to do with whether code (application, frameworks, libraries) targets Java 9 - it only depends on which path you place a JAR, on the class path or the module path.
If it's on the class path, it ends up in the unnamed module together with other class path content. This is true for plain JARs without module descriptor but also for modular JARs that contain one.
If it's on the module path, it gets its own module. If it's a modular JAR, it gets an explicit module as those described throughout the State of the Module System - plain JARs get turned into automatic modules (note the plural: one automatic module per JAR).
Do I understand right that there is always only one unnamed module in JPMS?
Yes, there is one unnamed module. The unnamed module is very similar to the existing concept of the unnamed package.
In implementations of the Java SE platform that use a hierarchical file system for storing packages, one typical strategy is to associate an unnamed package with each directory; only one unnamed package is observable at a time, namely the one that is associated with the "current working directory". The precise meaning of "current working directory" depends on the host system.
Does it mean that applications that were developed before Java9 and not updated for Java9 will be loaded as one unnamed module?
Yes, for those jars placed on the classpath would be treated as a single unnamed module. The bottom up migration with the concept of unnamed modules illustrates this with a similar example as:
Suppose, e.g., that the application shown above had originally been
built for Java SE 8, as a set of similarly-named JAR files placed on
the class path. If we run it as-is on Java SE 9 then the types in the
JAR files will be defined in the unnamed module.
The actual question that can arise here is
Which class loader is the unnamed module associated?
The State of Module System about unnamed module states a clarification instead about this.
Every class loader, it turns out, has its own unique unnamed module,
which is returned by the new ClassLoader::getUnnamedModule method.
If a class loader loads a type that is not defined in a named module then
that type is considered to be in that loader’s unnamed module, i.e.,
the getModule method of the type’s Class object will return its
loader’s unnamed module. The module colloquially referred to as “the
unnamed module” is, then, simply the unnamed module of the application
class loader, which loads types from the classpath when they are in
packages not defined by any known module.
The ClassLoader as revised in Java-9 states that:
The Java run-time has the following built-in class loaders:
Bootstrap class loader: The virtual machine's built-in class loader...
Platform class loader: ...
To allow for upgrading/overriding of modules defined to the platform
class loader, and where upgraded modules read modules defined to class
loaders other than the platform class loader and its ancestors, then
the platform class loader may have to delegate to other class loaders,
the application class loader for example. In other words, classes in
named modules defined to class loaders other than the platform class
loader and its ancestors may be visible to the platform class loader.
System class loader: It is also known as application class loader and is distinct from the platform class loader. The system
class loader is typically used to define classes on the application
class path, module path, and JDK-specific tools. The platform class
loader is a parent or an ancestor of the system class loader that all
platform classes are visible to it.
Related
I am new to java module system. I am trying to understand how JPMS decide the root modules to build the module graph at runtime.
What I understand is that all modules in the module path will be added to the set of root modules. JPMS start build the module graph from the root module set and add-modules option can add more modules to the root modules set.
When build the module graph if there is a class not found in all modules then JPMS will search for it in the classpath, if it's found then this class will be add to unnamed module.
The default root module set depends whether you your main class is located on the class path or the module path.
In any case, the modules on the module path are not automatically added to the set of root modules, unless you explicitly specify --add-modules ALL-MODULE-PATH.
If running from the classpath, most of the system modules are root modules. See JEP 261 for more details.
If running from the modulepath, only the module of the main application (as specified by --module is root by default. Others may be added with --add-modules.
From book <<java 9 revealed: for early adoption and migration>> Chapter2:
If the application code is compiled from the class path or the main class is run from
the class path, the default set of root modules consists of the java.se module and all
the non-java.* system modules such as jdk.* and javafx.*. If the java.se module
is not present, the default set of root modules consists of all java.* and non-java.*
modules.
If your application consists of modules, the default set of root modules depends on
the phase:
At compile-time, it consists of all modules being compiled.
At link time, it is empty.
At runtime, it contains the module that contains the main class. You use the
--module or -m option with the java command to specify the module and its
main class to be run.
Both Sealed Packages/Jars and the Java Module System disallow spliting packages across several jars.
Does that mean that all packages contained within a Module are implicitly sealed? If not what does explicitly sealing the jar change?
Yes, packages within modules are always implicitly sealed. This is specified in the documentation of the Package class:
A Package automatically defined for classes in a named module has the following properties:
The name of the package is derived from the binary names of the classes. Since classes in a named module must be in a named package, the derived name is never empty.
The package is sealed with the module location as the code source, if known.
The specification and implementation titles, versions, and vendors are unspecified.
Any annotations on the package are read from package-info.class as specified above.
I also made a quick test to verify that isSealed() on a module’s package did indeed return true. However, it must be noted that the relationship between (named) modules and packages is of a fundamental nature and hence, independent of the fact that isSealed() returns true. The latter is just the natural way for the old API to interact with this new feature.
A sealed package of an unnamed module only affects the runtime package of the particular class loader, as each class loader can have a package of the same name, which is considered a different runtime package.
In contrast, each package of a named module must unambiguously belong to a single module within the entire module layer and a module layer can span multiple class loaders, e.g. the boot layer spans the bootstrap loader, the platform loader, and the application loader.
This affects the class loading process. The old way of loading, which is still used for the unnamed module, features class loader delegation where each loader queries its parent loader first. When the parent loader failed, the class path entries are queried linearly for the class until a class is found.
When a module layer is initialized, all package names and their owning modules are recorded, so when an attempt to load a class is made, the package name can be derived from the qualified name and mapped to a module even before the class is loaded and therefore, only the module’s known source needs to be queried.
We have a plugin system with multiple module layers:
The PluginLoader module inside the boot layer instantiates a custom ModuleLayer for each Module at runtime with ModuleLayer::defineModulesWithOneLoader.
We now have a set of non-modular plugins that need access to the modular plugins inside the custom layers. They will be loaded after the custom layers have been instantiated. How can this be achieved?
Simply putting them on the classpath doesn't work, since the modular classes will be loaded a second time instead of using the (already loaded) classes from the modular plugins.
Our next try was creating a dummy plugin and loading it last inside a layer that has all other layers (and therefore modular plugins) as parents. It doesn't seem to be possible to add URLs to the class loader generated by the defineModulesWithOneLoader method. Instead we created a custom URLClassLoader as a parent of this class loader and added the non-modular JARs via URLClassLoader::addURL.
This works for the non-modular plugin classes. However, if a class already loaded in one of the modular plugins is referenced from such a class, it will also attempt to load the class with our custom URLClassLoader (which fails, since the parent is null) instead of the module's internal class loader, which delegates to parent layers. Is there a way to load our non-modular plugin's classes inside the unnamed module of a class loader that behaves like the JPMS' default class loader (search parent layers first) but still loads the classes inside the non-modular JAR, if they aren't found in the parent layers?
It is possible to solve this by using automatic modules, but we want to avoid this for now since it leads to a slew of other necessary changes (e.g. resolve split packages) which we currently don't have the resources for.
I have two JPMS layers:
Boot layer with module A loaded by ClassLoaders$AppClassLoader#4fca772d
Child layer with module B that provides cervices and loaded by Loader#6b58b9e9
The parent classloader of Loader#6b58b9e9 is ClassLoaders$AppClassLoader#4fca772d.
In module A I have the following code:
ServiceLoader<ModuleAInterface> sl = ServiceLoader.load(ModuleAInterface.class);
However, the services of Module B are found only when context class loader is Loader#6b58b9e9 and not found when context class loader is ClassLoaders$AppClassLoader#4fca772d.
The question - is it possible to get services of module B in module A without knowing class loader of module B in such configuration.
looking at the code of java.util.ServiceLoader in jdk 14 (see screenshot) it looks like it follows the same logic as class loading when there are multiple ModuleLayer, as described in this stackoverflow answer
which means that ServiceLoader will will first look at services in its own ModuleLayer then in its parent ModuleLayer and continue from child to parent in a recursive manner
is it possible to get services of module B in module A without knowing class loader of module B in such configuration.
no
but module B can see the services in module A
I am so confused about these two class loaders. When talking about the hierarchy of Java class loaders, usually the bootstrap classloader and ext class loader and the third one (system classloader or app classloader) are mentioned.
To be more accurate, I checked the source code of JDK. In class Launcher, there is the code:
loader = AppClassLoader.getAppClassLoader(extcl);
In class ClassLoader, the method:
getSystemClassloader()
Also says the system classloader is used to start the application.
So which is the third one in the hierarchy, or are the two classloaders the same?
Both AppClassLoader and SystemClassLoader are same.
Have a look at hierarchy.
ClassLoader follows three principles.
Delegation principle
Bootstrap ClassLoader is responsible for loading standard JDK class files from rt.jar and it is parent of all class loaders in Java. Bootstrap class loader don't have any parents.
Extension ClassLoader delegates class loading request to its parent, Bootstrap and if unsuccessful, loads class form jre/lib/ext directory or any other directory pointed by java.ext.dirs system property
System or Application class loader and it is responsible for loading application specific classes from CLASSPATH environment variable, -classpath or -cp command line option, Class-Path attribute of Manifest file inside JAR.
Application class loader is a child of Extension ClassLoader and its implemented by sun.misc.Launcher$AppClassLoader class.
Except Bootstrap class loader, which is implemented in native language mostly in C, all Java class loaders are implemented using java.lang.ClassLoader.
Have a look at this blog for better understanding of these three class loaders.
Visibility Principle
According to visibility principle, Child ClassLoader can see class loaded by Parent ClassLoader but vice-versa is not true.
If class Abc is loaded by Application class loader then trying to load class ABC explicitly using Extension ClassLoader will throw java.lang.ClassNotFoundException
Uniqueness Principle
According to this principle a class loaded by Parent should not be loaded by Child ClassLoader again
System class loader is a different name for Application class loader.
Source:
https://blogs.oracle.com/sundararajan/entry/understanding_java_class_loading
Application class loader ... is also (confusingly) called as "system class
loader" - not to be confused with bootstrap loader which loads Java "system"
classes.
The third in the class loader hierarchy is the SystemClassloader. It is also referred as ApplicationClassloader (or AppClassLoader) at some places. This loader loads the our application code and classes found in the classpath.
Regarding the below method in the Classloader:
public static ClassLoader getSystemClassLoader()
Javadoc says:
Returns the system class loader for delegation. This is the default
delegation parent for new ClassLoader instances, and is typically the
class loader used to start the application.
The important piece here is
This is the default delegation parent for new ClassLoader instances, and is typically the class loader used to start the application
Which means, if we create our own Custom or new classloader in our application, the System or Application class loader becomes the parent for our Custom or new classloader. And calling the getSystemClassLoader() method in Custom or new Classloader returns the System(aka Application) classloader. This aligns with the java class loader delegation model as well.
And the System (aka Application) class loader is the one which has loaded our class or app from classpath.