JEP 275: Modular Java Application Packaging
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.
Use jlink
to generate embedded Java Run-time and Application Images
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.