Yesterday we wrote about the delay to the Java 9 GA, which was in part due to the need for weak modules. This article discusses weak modules. It takes heavily from the March update to Project Jigsaw The State of the Module System (SOTMS) by Mark Reinhold, Oracle’s chief architect of the Java platform, and his proposal on Monday to introduce weak modules.

The state of the module system

Modules can import other modules and export their packages. When module A depends directly on module B in the module graph, it is possible for code in packages of module A to refer to public code in packages of module B. Module B is readable by module A. For the public types in a package of module B to be accessible to types in packages of module A, module B must export that package. So for code to be accessible outside its module, it must be public and in an exported package. Every module depends on (and so reads) the base module.

To help with backwards compatibility, for code that is not in a package in a named module, there is a special module: the unnamed module. Types in packages not defined in any known module are loaded from the class path, and are assumed to be in this unnamed module. The unnamed module reads every other module, and exports all of its packages. However named modules cannot access types in the unnamed module.

Frameworks

Frameworks (e.g. service loaders, resource bundles etc) need access to one of their own constructors to instantiate themselves. In an example from SOTMS, the platform’s streaming XML parser loads and instantiates the implementation of the XMLInputFactory service in the java.xml module:

String providerName
    = System.getProperty("javax.xml.stream.XMLInputFactory");
if (providerName != null) {
    Class providerClass = Class.forName(providerName, false,
                                        Thread.getContextClassLoader());
    Object ob = providerClass.newInstance();
    return (XMLInputFactory)ob;
}
// Otherwise use ServiceLoader
...

Copyright © 2016, Oracle and/or its affiliates.
All rights reserved.

In this example, invoking Class::forName still works so long as the package containing the provider is known to the context class loader. However the invocation of the provider class’s constructor (using the newInstance method) doesn’t work. This is as the java.xml module only depends on the base module, so whether the provider is in the unnamed module or a named module, it is not readable by the framework’s module.

Somehow, the provider class needs to be accessible to the framework. The provider’s module needs to be readable by the framework’s module.

The problem is how to do this without dictating changes to every framework. Rather than doing this, the reflection API was changed to assume that any code reflecting on some type is in a module that can read the module defining the type. The type still needs to be public and in an exported package in order to be accessed from outside its module.

The problem with frameworks…

However some framework libraries (like dependency injection and persistence) require reflective access to some members of non-exported types of other modules. This problem is outlined in Monday’s proposal on the OpenJDK mailing list. Examples discussed here are taken from the proposal.

Weak Modules

The solution proposed is the introduction of weak modules. Rather than explicitly exporting every package, by defining a module as weak, at compile and run time every package is exported. Non-public elements are available for deep reflection via the AccessibleObject::setAccessible method of the core reflection API.

To declare a weak module, insert the modifier ‘weak’:

    weak module foo.bar {
        // No exports
        requires hibernate.core;
        requires hibernate.entitymanager;
    }

Copyright © 2016, Oracle and/or its affiliates.
All rights reserved.

Exports cannot be made explicitly.

To migrate existing code to a weak module it needs to be bundled together in a module format (e.g. no duplicate packages) with an explicit ‘weak’ module declaration.

Transitioning from weak to strong

If further encapsulation is possible and weak modules can be made into a ‘strong’ module with exports, the weak module step makes the transition easier.

Weak modules do not differ from strong modules in any other way than the implicit export of all packages, and its making non-public types available for deep reflection. To transition from weak to strong, as well as explicitly exporting packages, strong modules can also enable deep reflection on non-public types. They can do this using the ‘private’ modifier on exports. For example:

    module foo.bar {
        exports private com.foo.bar.model;
        requires hibernate.core;
        requires hibernate.entitymanager;
    }

Copyright © 2016, Oracle and/or its affiliates.
All rights reserved.

Here any public or non-public entity classes in com.foo.bar.model can be accessed. Other packages are strongly encapsulated. The implicit export in weak modules is effectively the same as the explicit ‘private’ export in strong modules for deep reflection. The main difference is that for weak modules, the implicitly exported types are also directly accessible.

Further enhancements

The ‘private’ modifier can also be used with qualified exports, e.g. exports from module B made explicitly to module A. This allows module A to access non-public types of B via deep reflection. It is not possible to have a qualified private export and a non-qualified export of the same package.

Issues

One problem with the new ‘private’ modifier is a potential confusion with the ‘public’ modifier.

    module foo.bar {
        exports private com.foo.bar.baz;
        requires public java.sql;
    }

Copyright © 2016, Oracle and/or its affiliates.
All rights reserved.

Here, the ‘private’ modifier means private elements of the com.foo.bar.baz package are accessible for deep reflection at run time. In comparison the ‘public’ modifier means that any module reading foo.bar also has implied readability of the java.sql module (so access to all of its exported types). To combat this, ‘public’ will be replaced with ‘transitive’, so the module declaration will be:

    module foo.bar {
        exports private com.foo.bar.baz;
        requires transitive java.sql;
    }

Copyright © 2016, Oracle and/or its affiliates.
All rights reserved.

Conclusion

Weak modules will make migrating to the modular system of Java 9 much easier. As well as features like the unnamed module and automatic modules, weak modules are a conscientious addition to Java 9 GA.

Java 9: Weak modules

| Java Language| 1,480 views | 0 Comments
About The Author
- Katharine is a Java developer by trade, turned Community & Content Manager for Voxxed. Helping developers learn and share knowledge. Contact me at kbe@voxxed.com with any news, articles or tutorials.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>