Skip to content
Danny Thomas edited this page Sep 17, 2026 · 1 revision

Tools

Modules make Java tools available through the standard Tool and ToolProvider service interfaces. ja runs them in the module context selected by the current directory. It provides the built-in fmt, test, and bench workflows; ja tool runs a named tool directly.

Module-aware tools declare the standard Java arguments they accept, allowing ja to supply only the module paths, selected modules, and runtime access they need. The native launcher packages service-provided tools as ordinary commands without wrapper scripts or Java launch syntax.

Running tools

Run ja tool without a name to list the JDK and module-provided tools available to the selected modules:

ja tool

Pass a tool name followed by its own arguments to run it:

ja tool jshell
ja tool jdeps
ja tool javap com.example.application.Main
ja tool junit

The working directory or -C selects the module context. Module-aware tools receive the module paths, selected modules, and other standard Java arguments described by their tool metadata. Tools that do not accept module context run in the current directory.

Bundled tools

A ja-enabled JDK bundles compatible versions of these tools:

  • jig — resolves module dependencies from Maven repositories
  • jfmt — formats Java source through ja fmt or the jfmt command
  • jist — finds Java declarations and source for ja doc and ja source
  • jdocserver — serves browsable API documentation for ja doc --browse

The bundled tools remain ordinary commands and share the same module and dependency context.

Providing tools from a module

A module provides a tool through a standard Tool or ToolProvider service:

module com.example.checks {
    provides java.util.spi.ToolProvider with com.example.checks.Check;
}

The tool is immediately available from the source tree:

ja tool check

The service declaration is enough for a basic tool. Implement OptionChecker when ja should infer the standard Java options accepted by the tool. Provide tool metadata when option spelling and arity do not fully describe the tool's module contract.

Tool metadata

Tool metadata is the command-line contract between a tool and ja. It lives at META-INF/com.netflix.tools/tools/<name>.properties. Metadata is unnecessary when a tool needs no module context.

When options is omitted, ja uses an OptionChecker implemented by the provider to determine which standard Java arguments the tool accepts. Declare options to replace that inference with a precise contract, listing option names without the leading --:

options=module-path,module=list,release,enable-preview,add-exports

ja resolves and supplies only the declared arguments. Use one module form within the options list:

  • module=single when the tool accepts exactly one selected module
  • module=list when the tool accepts a comma-separated module list
  • module=main when the tool expects module/main-class
  • module=roots when the tool accepts one root through --module and multiple roots through --add-modules

module=roots must be accompanied by add-modules, and bare module is an alias for module=single. The forms are mutually exclusive. Metadata supplies this operand-shape information because OptionChecker can report only whether --module is supported and how many arguments it consumes.

The fixed junit tool used by ja test demonstrates activation and Java launching:

launch=java
activation=org.junit.platform.engine
module=org.junit.platform.console
defaults=execute --details=none --disable-banner --disable-ansi-colors
class-suffix=Test
package-suffix=test

This metadata makes junit applicable when org.junit.platform.engine is present in the selected module graph. The module property identifies the module containing the tool, while launch=java runs that module in a child JVM instead of invoking an in-process provider. defaults supplies arguments before those passed by the user. Provider launch is the default for other tools.

class-suffix and package-suffix identify tool-specific content when packaging a module. If the activation dependency is static, matching classes, packages, and resources are left out of jar, jmod, and Maven artifacts. An ordinary requirement keeps that content in the artifacts.

Include verbose in options when the tool accepts --verbose. ja passes its global --verbose switch to tools that declare this support or expose it through OptionChecker.

Use provider when the service name differs from the metadata name. Append @version to module to select a fixed provider version; otherwise the version of the activation module is used.

Native launcher

A native launcher turns a service-provided tool into an ordinary command without a wrapper script, class path, or Java launch syntax. Tools bundled with a ja-enabled JDK use these launchers.

Exporting a module that provides Tool or ToolProvider services produces platform-specific jmod artifacts containing their native launchers. Linking one of those artifacts into a JDK image makes its tools available as ordinary commands.

Launcher configuration carries the target module and the runtime arguments required by the tool. Preview features and native, final-field, exported-package, and open-package access are preserved when tools are packaged and runtime images are linked. Users do not need to reproduce those settings as JVM flags.

Installed application launchers

ja install gives every application a native launcher, even when the application only declares a main class and does not provide a Tool or ToolProvider service.

For an application module named <module>, installation generates a companion module named <module>.launcher. That name is reserved for the installed command adapter. The companion module requires the application and com.netflix.tools.launcher, and it becomes the root used to launch the command.

When the application already provides a tool whose name matches the installed command, the companion module anchors that service in the runtime image. Otherwise, it provides a generated Tool adapter that invokes the application's declared main class. The main package is opened only to the companion launcher module.

Every installed application receives -- passthrough and launcher options. Version reporting, argument files, and working-directory handling are tool-level conventions rather than native-launcher behavior. Installation also preserves preview and runtime-access configuration in the generated command. Tools that provide startup and warmup metadata additionally receive launcher-managed AOT caching.

Startup and warmup

A service-provided tool can declare arguments that exercise its startup path:

warmup=--aot-warmup

A native launcher packaged for the tool uses these arguments to create an AOT cache before running the requested invocation. Later launches reuse the cache, which is rebuilt when the runtime image or launcher configuration changes. The JDK installer can create these caches while building a development image.

AOT caching is automatic on supported Java 25 or later HotSpot images and on OpenJ9 images. Launcher options can override it:

  • -L-aot=auto uses or creates a cache, falling back to a normal launch if warmup is unavailable
  • -L-aot=create creates or refreshes the cache and reports any warmup failure
  • -L-aot=off runs without the launcher-managed cache

HotSpot uses a user cache and stores warmup output and VM logs beside it as out and log. Automatic creation is attempted once for each runtime and launcher configuration. If it fails, an attempted marker prevents later launches from repeating the warmup; the launcher reports the marker path so it can be removed to retry.

OpenJ9 uses one shared class cache under lib/ja/sharedclasses in the runtime image. Warmup opens it for updates and ordinary launches reuse it read-only. The launcher resolves the cache from the runtime image so it continues to work when the image moves. A failed automatic warmup leaves lib/ja/sharedclasses.attempted; remove it or use -L-aot=create to retry.

Launcher arguments

Launcher options such as -L-aot=off are interpreted before the launched command's arguments. Use -- to stop launcher option processing when the command needs to receive an argument that looks like a launcher option:

some-command -- -L-aot=off

The native launcher passes @file, -C, and --version through unchanged. Tools may opt into Java-style argument files, working-directory handling, and module-version reporting as part of their described command-line contract; tools that do not opt in receive those arguments literally.

Clone this wiki locally