jpx
Execute.java builds and runs your project. jpx runs someone else's: point it at a published module
or Maven artifact and it resolves the dependency closure, installs it once under your home directory, and
launches the main entry point. It is npx for the module path - a way to run a released tool without
cloning, building, or writing a class path by hand.
java build/jenesis/Jpx.java org.junit.platform.console --version
That resolves the JUnit console launcher and its dependencies, installs them, and runs the tool - passing
--version straight through to the launched program.
PATH as a plain jpx command - jpx
org.junit.platform.console --version is the same thing without the java
build/jenesis/Jpx.java prefix. The examples below use the jpx form.
The target grammar
The first argument names what to run. Its full form is:
<name>[@<version>][/<main-class>]
Only the name is required. The three parts each answer one question: what, which version, and which entry point.
The name - module or Maven coordinate
The name is resolved one of two ways, and jpx tells them apart by a single rule: a module name can never contain a colon.
- A Java module name - e.g.
org.junit.platform.console. jpx discovers its Maven coordinates as a POM through repo.jenesis.build and reads the dependency graph from Maven metadata, exactly as themodular_to_mavenlayout does when it resolves arequiresname. - A
<groupId>:<artifactId>pair - e.g.org.apache.commons:commons-lang3. The colon marks it as Maven coordinates, resolved directly.
jpx org.junit.platform.console # by module name
jpx org.junit.platform:junit-platform-console # by Maven coordinate
The version - which release
Append @<version> to pin a release:
jpx org.junit.platform.console@1.11.0
Without a version, jpx prefers the most recently installed version of that target; failing that, it resolves the latest release. So the first run pulls the current release and later runs reuse it until you ask for a newer one.
The main class - which entry point
By default jpx launches the jar's module main class or its Main-Class manifest attribute. Append
/<main-class> to choose a different entry point:
jpx com.example.tool/com.example.tool.alt.Cli
This works exactly like java -m <module>/<main-class>, which also means a jar that declares no entry
point at all is still runnable - just name the class yourself.
Options
The remaining flags mirror the rest of the tool.
| Flag | What it does |
|---|---|
--modular |
Resolve purely over module descriptors, walking requires clauses like the modular layout - every module must be explicitly named. |
--docker[=<image>] |
Run the launched process in a container while resolution and installation stay on the host. |
--hash=<prefix> |
Verify the installed jars against a known digest before launching (see below). |
Running in a container
--docker isolates only the launched program, not the resolution and installation, which stay on the host:
jpx --docker org.junit.platform.console --version
The installation folder and the host's Java home are mounted read-only, so the containerized run needs no
network and no credentials of its own. Pass --docker=<image> to choose the image; with none, a minimal
hardened image is used. This is the same launch-side isolation that Execute.java offers for your own
project - see Build performance & isolation.
Verifying the installation
--hash=<prefix> re-checks the installed jars against a digest you already trust, before every launch:
jpx --hash=3f9a1c… org.junit.platform.console --version
The prefix must be at least 32 hex characters of the target's SHA-256 digest (see below). A mismatch aborts the launch, catching both a tampered download and a tampered installation on disk.
Where installs live
Each resolved target installs to:
~/.jenesis/jpx/<name>@<version>/
The folder holds the closure's jars in one flat directory beside a jpx.properties descriptor that records
the module path, the class path, the entry point, and a deterministic SHA-256 digest over all the jars -
the same digest --hash checks against.
The descriptor is written last, on purpose: a download that crashes mid-way leaves no descriptor, so jpx
recognizes the install as incomplete and redoes it rather than launching a half-populated folder. Two
processes installing the same target coordinate through a file lock, so concurrent jpx invocations do
not collide.
Usage
Running jpx with no arguments - or with --help - prints the usage screen:
jpx --help