JEP 275: Modular Java Application Packaging

AuthorDanno Ferrin
OwnerChris Bensen
Created2015/05/15 19:33
Updated2016/08/18 20:01
TypeFeature
StatusCompleted
Componentdeploy / packager
ScopeJDK
Discussionopenjfx dash dev at openjdk dot java dot net
EffortM
DurationM
Priority3
Reviewed byAlan Bateman, Kevin Rushforth, Mandy Chung
Endorsed byKevin Rushforth
Release9
Issue8080531
Relates toJEP 282: jlink: The Java Linker

Summary

Integrate features from Project Jigsaw into the Java Packager, including module awareness and custom run-time creation.

Motivation

The Java Packager (javapackager) has always generated huge binaries when it is asked to bundle a run-time as part of its packaging due to the size of the JRE. Project Jigsaw will develop a tool defined in JEP 282 jlink: The Java Linker that allows creation of run-time images that contain a subset of the standard and JDK modules enabling the Java Packager to reduce the size of the bundled runtime image.

Description

For the most part, the Java Packager workflow will stay the same. New tools from Jigsaw will be added, and in some cases replace some steps.

Only Generate Java 9 Applications

The Java Packager will only create applications that use the JDK 9 run-time. This will simplify a lot of code paths and assumptions with regard to tools used to assemble applications and java run-times. If a user wants to create a Java 8 application then the Java 8 version of Java Packager shipped with JDK 8 will continue to work. We assume that the number of self-contained applications that need to work simultaneously on Java 8 and Java 9 will be essentially zero, since the application brings its own JVM with it.

Currently JREs are copied and unneeded portions are deleted from the copied run-time.

The Java linker tool, jlink, provides a means to generate a JRE image that contains only the required modules. Furthermore jlink may expose some hooks for its image-generation process that we may take advantage of to further customize the image by, for example, adding the removal of executables to the jlink processing, or compression.

The Java Packager will call jlink to create an application run-time image that will be embedded in the application image. The Java Packager will fail with an appropriate error if jlink fails. It is expected that the packaged modules will ship with JDK 9.

The jlink tool includes a plugin and extension mechanism. When using jlink to generate the application image we will integrate with those mechanisms so that the output of the jlink process is the application image in a proper platform-specific layout. This will have the desirable side effect of making application image generation not dependent on the Java Packager process.

javapackager CLI Arguments, Ant Tasks and Java Packager API

The Java Packager has new CLI arguments to match the rest of the Java toolchain specified in JEP 261 for option syntax and values:

--add-modules <module>(,<module>)*
--limit-modules <module>(,<module>)*
--module-path <path>(:<path>)*
-p <path>(:<path>)*
--module <module>/<classname>
-m <module>/<classname>

To specify an argument for a long option, you can use --<name>=<value> or --<name> <value>.

NOTE: --module-path maps to jlink's --module-path but with an optional default value. More information below.

There will be new ANT tasks off the <fx:application>, <fx:secondaryLauncher> and the new <fx:runtime> task.

For example:

<fx:deploy outdir="${bundles.dir}"
           outfile="MinesweeperFX"
           nativeBundles="all"
           verbose="true">

    <fx:runtime strip-native-commands="false"> <-- new
        <fx:add-modules value="java.base"/>
        <fx:add-modules value="jdk.packager.services,javafx.controls"/>
        <fx:limit-modules value="java.sql"/>
        <fx:limit-modules value="jdk.packager.services,javafx.controls"/>
        <fx:module-path value="${java.home}/../images/jmods"/>
        <fx:module-path value="${build.dir}/modules"/>
    </fx:runtime>

    <fx:application id="MinesweeperFX"
                    name="MinesweeperFX"
                    module="fx.minesweeper" <-- new
                    mainClass="minesweeper.Minesweeper"
                    version="1.0">
    </fx:application>

    <fx:secondaryLauncher name="Test2"
                          module="hello.world" <-- new
                          mainClass="com.greetings.HelloWorld">
    </fx:secondaryLauncher>
</fx:deploy>

<fx:runtime>, <fx:limit-modules>, <fx:add-modules>, <fx:modular-path> are optional arguments. The module="module name" argument on <fx:application> is used if bundling with a modular application, otherwise if the application is a non-modular application it is invalid. The arguments <fx:limit-modules>, <fx:add-modules>, <fx:modular-path> are interchangeable with --add-mods, --limit-mods and --module-path used in this document. See the section Module Configurations for additional module are argument information.

The Java Packager API will get new methods for modular options.

Strip Native Commands

Stripping the commands such as java.exe has been the default for the Java Packager but some developers need the command line tools such as java.exe. So there will be an option to include the native commands by turning off the removal of stripping of commands:

--strip-native-commands false

Add support for modules and module paths

Jigsaw introduces the notion of a "module path" in addition to a classpath. The module path consists of paths to the libraries, JDK modules and the application module. The paths that contain these modules are specified with the command line argument:

--module-path <path>(:<path>)*

It can be supplied only once and it is a platform path. The root modules and their transitive dependences are linked to create a modular run-time image (JEP 220).

The developer can supply a path with packaged modules to bundle with a different version of the Java Runtime than the default. If no JDK packaged modules are provided by the developer then the Java Packager will default to using the packaged modules supplied with the version of the JDK that the Java Packager ships with ($JAVA_HOME/jmods).

The Java Packager does not currently provide a mechanism to copy packaged modules to the application run-time image instead of linking into the jimage. The most likely need for this scenario would be if the application supports plugins and these modules live outside of the bundled image. If that is the case, the developer will need to override the --module-path and --add-modules using the user JVM argument overrides.

Module Configurations

There are two types of Java applications that will be bundled using the Java Packager: Non-modular JARs and Modular Applications.

Non-modular JARs consist of a JAR without a module-info.class in the JAR file. Use -appClass and -BmainJar=. for applications. Developers will use the Java Packager with the same arguments as with previous versions prior to JDK 9 using the -srcfiles, -Bclasspath=, -appClass and -BmainJar= arguments. For backwards compatibility no new modular arguments are required and by default the embedded Java Runtime will consist of all redistributable modules, so there will be no size reduction of the bundled runtime. Developers can use --module-path, --add-modules and --limit-modules to include 3rd party modules.

For example:

javapackager -deploy -v -outdir output -name HelloWorld -Bclasspath=hello.world.jar -native -BsignBundle=false -BappVersion=1.0 -Bmac.dmg.simple=true -srcfiles hello.world.jar -appClass HelloWorld -BmainJar=hello.world.jar

Modular Applications consist of a JAR, exploded module, or packaged module containing a module-info.class. To bundle with a Modular Application the --module and --module-path arguments must be specified. --module is mutually exclusive to -appClass and -BmainJar=. --module-path must provide a path containing the main module (the module referenced with --module). Other modules can be added to the run-time image using --add-modules and --limit-modules. Modules dynamically loaded through core reflection or services must be manually specified with --add-modules. The main module and the modules provided by --add-modules will define the root modules. jlink will create a run-time image with the specified root modules and their transitive dependencies.

For example:

javapackager -deploy -v -outdir output -name Test -native -BsignBundle=false -BappVersion=1.0 -Bmac.dmg.simple=true --module-path /path/to/jmod --module hello.world/com.greetings.HelloWorld

This command will produce a run-time image consisting of the main module and all of its transitive dependencies. Other modules can be added via --add-modules option.

Modules

The packager will be split into two modules:

jdk.packager
jdk.packager.services

jdk.packager contains the Java Packager that builds the app bundle and the installers. jdk.packager.services is a module that is bundled with the app bundle that provides access to packager services at runtime such as JVM user arguments.

JNLP

The bundles that are generated will depend on the input and the options provided. Historically -deploy would generate all native bundles and .jnlp files. Now, -deploy in conjunction with -module will not generate .jnlp files since JNLP does not support the new modular options. -native with no options will generate all native bundles available.

Testing

First and foremost, existing API, command line, and Ant invocations of the Java Packager that worked in JDK 8 should work in JDK 9, so existing tests for the JDK 8 packager should be run.

New tests will need to be written to exercise the new flags exposed to to support run-time image generation, module-path specifications, and jeeps process interactions.

Risks and Assumptions

We assume that the project will be delivered substantially as it has been described. If large functional parts are moved to later releases, such as the module path and module system, then the corresponding portions of this JEP will slip as well.

Dependences