Producing a launcher jar
You never assemble a launcher jar by hand. The Jenesis build tool produces it from a switch in
packaging.properties, shading the launcher into the jar and laying out your dependencies for it. This
chapter shows what that switch produces: the assembly the bundler performs, the resulting jar layout, and the
manifest entries that make java -jar app.jar start the launcher.
Turning it on
The launcher jar is one of the build tool's packaging options. Enable it
by setting launcher=true in a packaging.properties file in the configuration
location:
# build.jenesis/packaging.properties
launcher=true
Like every packaging feature, it only runs for a module that declares a main class - the same
@jenesis.main tag (or <mainClass> POM property) the other packaging steps key off. The build then wires a
launcher step into the package phase and writes one executable jar per runnable module:
target/.../launcher/<name>.jar
What the bundler assembles
The build resolves the published Jenesis Launcher artifact and produces the jar in four moves. Everything the launcher needs at run time - the layout described in How it works - is put in place here:
- Shade the launcher into the jar root. The launcher's own
build/jenesis/launcher/*.classfiles are copied to the jar root, with the launcher'smodule-infoand manifest dropped, so at run time they are the unnamed module hosting your application. - Explode each dependency into its own subfolder -
classpath/<name>/for a non-modular dependency,modulepath/<name>/for a modular or automatic one, using the same modular split theExecutelauncher and a bundle use.<name>is the dependency's original jar file name, so automatic-module naming, which the JDK derives from that name, is unchanged. - Set the manifest
Main-Classtobuild.jenesis.launcher.Launcher, sojava -jarstarts the launcher. - Write
application.properties- the descriptor from How it works, carryingmainClass,mainModulewhen the application is modular, the class-path order, and (when present)agentClass.
classpath/ and modulepath/ subfolders and an application.properties.
The launcher jar folds it into a single runnable jar with the launcher shaded in, so it needs no launch
script; a bundle keeps the files separate for you to drop onto a JRE base.
The produced jar layout
The result is an ordinary jar - every class and resource is a direct entry - with a fixed shape the launcher knows how to read:
foo.jar
├── META-INF/MANIFEST.MF Main-Class: build.jenesis.launcher.Launcher
├── build/jenesis/launcher/… the shaded launcher classes
├── application.properties mainClass, mainModule, classpath order, agentClass
├── classpath/
│ └── <dependency-jar-name>/… a non-modular dependency, exploded
└── modulepath/
└── <module-jar-name>/… a modular or automatic dependency, exploded
Nothing is merged: each dependency keeps its own module-info, META-INF/services files, and resources in
its own subfolder. That is what lets the launcher rebuild the module graph at startup - see How it
works for how it reads this jar.
The manifest wiring
Two manifest attributes are all that connect java -jar to the launcher.
Main-Class names the launcher, so the JVM invokes it and the launcher then finds and runs your real main
class from application.properties:
Main-Class: build.jenesis.launcher.Launcher
When the descriptor carries an agentClass - the application bundles its own Java agents - the bundler adds a
second attribute so the JVM hands the launcher a real Instrumentation before main runs:
Launcher-Agent-Class: build.jenesis.launcher.LauncherAgent
Without it, the JVM captures no Instrumentation and only agents that need none can run. The full set of
agent and access-control descriptor keys is covered in the Reference chapter.
Class-path order is preserved
A class path is ordered: when two jars carry the same class or resource, the first one wins. Exploding the
dependencies into subfolders would lose that order, so the bundler records it in a classpath property of
application.properties - a comma-separated list of class-path dependency names:
mainClass=com.example.Main
classpath=dep1.jar,dep2.jar
The launcher orders its class path by this list; any dependency the property does not name follows in dependency-name order. You never write this by hand - the build captures the resolved order for you.
The launcher is pinned like any dependency
The Jenesis Launcher is resolved as a normal dependency, in its own launcher group, and is
pinned like every other artifact the build uses. The exact launcher bytes shaded into
your jar are therefore verified, and the produced jar stays reproducible - the same sources yield the same
bytes.
With the jar produced, the next chapter turns to running it: the start-up flow, the single-loader consequences, and the pitfalls to watch for.