https://github.com/bsels/javafx-maven-plugin
A lightweight Maven plugin based on the JavaFX Maven Plugin with three goals: fxml‑source generates type‑safe Java classes from FXML at compile time; run launches the app for development; jlink builds a minimal self‑contained runtime image. Requires Java 25+, a modular project, and provides config for packages, debugging, logging, native binaries.
https://github.com/bsels/javafx-maven-plugin
java javafx javafx-maven-plugin javafx-modules maven maven-plugin plugin source-code-generation
Last synced: about 15 hours ago
JSON representation
A lightweight Maven plugin based on the JavaFX Maven Plugin with three goals: fxml‑source generates type‑safe Java classes from FXML at compile time; run launches the app for development; jlink builds a minimal self‑contained runtime image. Requires Java 25+, a modular project, and provides config for packages, debugging, logging, native binaries.
- Host: GitHub
- URL: https://github.com/bsels/javafx-maven-plugin
- Owner: bsels
- License: mit
- Created: 2025-11-21T17:01:04.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-04-18T14:19:35.000Z (8 days ago)
- Last Synced: 2026-04-18T14:26:08.997Z (8 days ago)
- Topics: java, javafx, javafx-maven-plugin, javafx-modules, maven, maven-plugin, plugin, source-code-generation
- Language: Java
- Homepage:
- Size: 1.01 MB
- Stars: 5
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Codeowners: .github/CODEOWNERS
- Security: SECURITY.md
Awesome Lists containing this project
README
# javafx-maven-plugin
[](https://github.com/bsels/javafx-maven-plugin/releases)
[](https://search.maven.org/artifact/io.github.bsels/javafx-maven-plugin)
[](https://github.com/bsels/javafx-maven-plugin/actions/workflows/push-release.yaml)
[](https://github.com/bsels/javafx-maven-plugin/actions/workflows/release-build.yaml)
[](https://opensource.org/licenses/MIT)

A concise Maven plugin that has been inspired from the JavaFX Maven Plugin
([openjfx/javafx‑maven‑plugin](https://github.com/openjfx/javafx-maven-plugin)) for the `jlink` and `run` goals.
## Features
| Goal | What it does | Typical use‑case |
|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`fxml-source`** | Converts each **FXML** file in the project into a corresponding **Java source file**. The generated classes expose the UI structure as plain Java code, which can be compiled together with the rest of the project. | When you want compile‑time safety for FXML bindings, avoid runtime loading of FXML resources, or need to ship a pure‑Java UI without external `.fxml` files. |
| **`jlink`** | Invokes the **`jlink`** tool to create a **stand‑alone, custom runtime image** that contains only the modules required by the application. The result is a self‑contained directory (or installer) that can be distributed and run on target machines without a pre‑installed JDK. | Packaging a production‑ready JavaFX application that should run out‑of‑the‑box on end‑users’ computers, minimizing size and eliminating the need for a separate JRE installation. |
| **`run`** | Executes the project in the development environment using the same launch logic provided by the upstream **JavaFX Maven Plugin**. It assembles the module path, sets up the JavaFX runtime, and starts the main class so you can iterate quickly while coding. | During development, to test UI changes, verify module configurations, or debug the application without building a full distribution. |
These three goals together give you a smooth workflow: generate type‑safe UI code (`fxml-source`), test and develop
rapidly (`run`), and finally produce a lean, portable executable bundle (`jlink`).
## Requirements—What does the plugin need?
- **Java version**: The codebase must be compiled and run on **Java 25 or later**.
This ensures access to the latest language features, module system improvements, and the `jlink` tool enhancements
introduced in recent JDK releases.
- **Modular architecture**: The application is built as a **Java module** (using `module-info.java`).
All source files, libraries, and resources must be declared in explicit modules, allowing the JDK to enforce strong
encapsulation and reliable configuration at compile time and run‑time.
- **FXML handling**:
- All **FXML files** reference **classes that are part of the compiled classpath** (i.e., they must already be
compiled into the module), or that can be compiled without the source code that needs to be generated
(optimistic compilation: compiles everything that can be compiled without errors).
- The **generic‑type references** used in those FXML files may reside in source code that has **not yet been
compiled**; the plugin will generate the necessary Java source files that will require those classes to be
part of the compilation classpath.
## fxml-source Maven Goal
The **`fxml-source`** goal converts JavaFX FXML files into plain‑Java source code during the **`generate‑sources`**
phase.
It is used to reduce runtime dependencies because the JavaFX FXML loader is not needed at runtime.
### What It Does
1. **Scans** one or more user‑specified directories for `*.fxml` files.
2. **Reads** each file into an intermediate representation (`FXMLReader`).
3. **Parses** the file into an `fxml.v2` document model (`FXMLDocumentParser`).
4. **Generates** Java source code (`FXMLSourceCodeBuilder`) that mirrors the FXML hierarchy (fields, methods, imports,
resource bundle lookups, etc.).
5. **Writes/updates** the generated `.java` files (existing files are overwritten) into a configurable output folder
and adds that folder to the project’s compiler source roots so the generated classes are compiled with the rest of
the project.
6. **(Optional)** Extends class discovery with an optimistic in‑memory compilation step so the parser can resolve as
many project classes as possible even if the project is not fully compiled yet.
### Key Configuration Parameters
| Parameter | Property | Required? | Default | Description |
|--------------------------------------|----------------------------------------|----------------|-----------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `project` | `${project}` | Yes (injected) | – | Maven project reference – used to add the generated source directory to the compile classpath. |
| `fxmlDirectories` | (n/a; XML config) | Yes | – | One or more `FXMLDirectory` entries that point to directories with `*.fxml` files. Each entry can also define an output package and exclusions (see below). |
| `resourceBundleObject` | `javafx.fxml.resourceBundleObject` | No | – | Fully‑qualified name of a resource‑bundle class used for i18n lookups inside the generated code. |
| `generatedSourceDirectory` | `javafx.fxml.generatedSourceDirectory` | Yes | `${project.build.directory}/generated-sources/fxml` | Destination folder for the generated Java files. |
| `includeSourceFilesInClassDiscovery` | `javafx.fxml.include.source.discovery` | No | `true` | When `true`, the plugin performs an optimistic in‑memory compilation step to include as many project classes as possible in the discovery classpath (helpful when FXML references project types/generics). |
| `defaultCharset` | `javafx.fxml.charset` | Yes | `UTF-8` | Character set used to read the FXML files and to drive parsing/generation. |
| `addGeneratedAnnotation` | `javafx.fxml.add.generated.annotation` | No | `true` | When `true`, the plugin adds an `@Generated` marker to generated sources. |
Each `FXMLDirectory` entry supports:
| `FXMLDirectory` field | Required? | Default | Description |
|-----------------------|-----------|--------------|-------------------------------------------------------------|
| `directory` | Yes | – | Directory containing FXML files to process. |
| `packageName` | No | (no package) | Package for the generated classes from this directory. |
| `excludedFiles` | No | (empty) | Paths (relative to `directory`) to exclude from processing. |
### Typical Usage in `pom.xml`
```xml
io.github.bsels
javafx-maven-plugin
2.0.0
generate-fxml-sources
fxml-source
generate-sources
${project.basedir}/src/main/resources/fxml
com.myapp.ui.generated
legacy/OldView.fxml
com.myapp.i18n.Messages
true
UTF-8
true
```
### When to Use It
- You want **type‑safe Java** access to UI components defined in FXML without a manual controller boilerplate.
- Your project relies heavily on **code generation** for UI scaffolding or wants to keep UI definitions declarative
while still benefiting from IDE navigation and refactoring.
- You need automatic i18n support via a resource bundle that is wired into the generated code.
### Tips & Gotchas
- **Package name:** If omitted, generated classes end up in the root of the generated‑source folder, which can cause
naming collisions.
- **Classpath:** Ensure any custom JavaFX controls or third‑party libraries used in the FXML are declared as Maven
dependencies; otherwise class loading will fail.
- **Scripts and expressions:** Scripts and expression bindings (e.g., ``, `${...}`) are not supported yet.
- **Incremental builds:** The plugin does not currently check timestamps, so it regenerates all files on each run.
Consider cleaning the generated folder only when necessary.
- **Use generics:** Generics need to be explicitly declared in the FXML file.
This is done by XML comment: ``, e.g.
``.
- **Declare interfaces:** If the generated class should implement one or more interfaces,
you must declare them in the FXML as XML comments.
The supported format is:
- ``
- ``
If the interface type can be resolved from the classpath, the plugin will reflectively discover its methods,
and you can omit the `methods` part.
If the interface type cannot be resolved during parsing (e.g., it is not yet compiled and cannot be found via class
discovery), you must provide the `methods` signature list in the comment so the generator can still bind method
references.
Example:
- ``
-
``
### Important changes in V2
This section describes differences between the current implementation (V2) and the README’s previous state (V1).
- **Configuration shape changed**: V2 uses `fxmlDirectories` (a list of `FXMLDirectory` entries) instead of single
`fxmlDirectory` + top‑level `packageName`.
- **Per-directory exclusions**: V2 adds `excludedFiles` per `FXMLDirectory` to skip specific FXML files.
- **Source discovery default changed**: V2 enables optimistic class discovery by default via
`includeSourceFilesInClassDiscovery` (`javafx.fxml.include.source.discovery`, default `true`).
- **New generation toggles**: V2 adds `defaultCharset` (`javafx.fxml.charset`) and `addGeneratedAnnotation`
(`javafx.fxml.add.generated.annotation`).
- **Removed/obsolete options**: V1’s `debugInternalModel` and `fxmlParameterizations` are not part of the V2 mojo
configuration surface.
### Example FXML File
```xml
```
---
With this configuration, running `mvn generate-sources` (or any later lifecycle phase) will automatically produce
ready‑to‑compile Java classes that mirror your FXML UI definitions, letting you treat the UI as ordinary Java code while
preserving the declarative benefits of FXML.
## jlink Maven Goal
The **`jlink`** goal creates a custom, minimal Java runtime image that contains only the modules required by your JavaFX
application.
It leverages the JDK’s `jlink` tool and adds JavaFX‑specific handling (native access, launcher scripts, additional
binaries, logging configuration, optional ZIP packaging, etc.).
### When to Use It
- You want a **stand‑alone executable** that ships only the Java runtime and the JavaFX modules your app needs—no
separate JRE installation required.
- You need to **reduce the size** of the distribution by stripping debug info, removing header files/man pages, and
applying compression.
- You require **custom launcher scripts** (extra JVM options, additional binaries, command‑line arguments) or a *
*pre‑configured logging format**.
- You want the final image optionally **zipped** for easy distribution.
### Key Configuration Parameters
| Parameter | Property | Default | Description |
|----------------------------|-----------------------------------|--------------|------------------------------------------------------------------------------------|
| `mainClass` | `javafx.mainClass` | **required** | Fully‑qualified main class (or `module/name` if it contains a slash). |
| `jlinkExecutable` | `javafx.jlinkExecutable` | `jlink` | Path or name of the `jlink` binary. |
| `jmodsPath` | `javafx.jmodsPath` | – | Directory containing JavaFX `jmod` files. |
| `jlinkImageName` | `javafx.jlinkImageName` | `image` | Folder name of the generated runtime image. |
| `jlinkZipName` | `javafx.jlinkZipName` | – | Base name for the optional ZIP archive (`${name}.zip`). |
| `launcher` | `javafx.launcher` | – | Name of the launcher script to create (`bin/`). |
| `options` | – | – | List of extra JVM options added to the launcher script. |
| `commandlineArgs` | `javafx.args` | – | Arguments appended to the launcher’s `$@` placeholder. |
| `additionalBinaries` | `javafx.additionalBinaries` | – | Files copied into `bin/` and exposed as `-D=./` in the launcher. |
| `stripDebug` | `javafx.stripDebug` | `false` | Adds `--strip-debug` to `jlink`. |
| `stripJavaDebugAttributes` | `javafx.stripJavaDebugAttributes` | `false` | Adds `--strip-java-debug-attributes`. |
| `compress` | `javafx.compress` | `0` | Compression level (`0‑2` or `zip‑0`-`zip‑9`). Invalid values abort the build. |
| `noHeaderFiles` | `javafx.noHeaderFiles` | `false` | Adds `--no-header-files`. |
| `noManPages` | `javafx.noManPages` | `false` | Adds `--no-man-pages`. |
| `bindServices` | `javafx.bindServices` | `false` | Adds `--bind-services`. |
| `ignoreSigningInformation` | `javafx.ignoreSigningInformation` | `false` | Adds `--ignore-signing-information`. |
| `jlinkVerbose` | `javafx.jlinkVerbose` | `false` | Adds `--verbose`. |
| `loggingFormat` | `javafx.loggingFormat` | – | Overrides `java.util.logging.SimpleFormatter.format` in `conf/logging.properties`. |
### Typical Usage in `pom.xml`
```xml
io.github.bsels
javafx-maven-plugin
2.0.0
jlink-image
jlink
process-classes
com.example.MainApp
jlink
${java.home}/jmods
my-app-image
my-app-runtime
myapp
-Xmx512m
-Dmy.prop=value
--theme dark
ffmpeg
${project.basedir}/native/ffmpeg.exe
ffmpeg.path
true
true
zip-9
true
true
true
false
false
%1$tF %1$tT %4$s %2$s - %5$s%6$s%n
```
### Example Build Output
```
target/
├─ my-app-image/
│ ├─ bin/
│ │ ├─ myapp ← launcher script (Unix)
│ │ └─ myapp.bat ← launcher script (Windows)
│ ├─ conf/
│ │ └─ logging.properties ← patched format
│ ├─ lib/… ← JavaFX modules + app modules
│ └─ …
└─ my-app-runtime.zip ← optional distribution archive
```
### Tips & Gotchas
- **Module Descriptor Required** – `jlink` will fail if the project does not produce a `module-info.class`.
- **Compression Values** – Only values present in `COMPRES_OPTIONS` (`zip‑0‑zip‑9` or deprecated `0-2`) are accepted; an
invalid value stops the build.
- **Custom `jmodsPath`** – Point this to the directory containing the JavaFX `jmod` files that match the JDK version you
are using.
- **Launcher Naming** – The `` name becomes the executable file name under `bin/`.
- **Additional Binaries** – Each binary is exposed to the application via a system property (`-D=./`). Use
this to load native libraries at runtime.
- **ZIP Packaging** – If you omit ``, the image remains unpacked; useful for Docker layers or when you
want to ship the directory as‑is.
### Further Reading
Official JDK jlink
documentation: [https://docs.oracle.com/en/java/javase/21/docs/specs/man/jlink.html](https://docs.oracle.com/en/java/javase/21/docs/specs/man/jlink.html).
---
Add the snippet above to your project's pom.xml and run:
```shell
mvn clean package
```
The plugin will generate a minimal Java runtime image containing only the modules your JavaFX application needs,
together with a ready‑to‑use launcher and optional ZIP distribution.
Enjoy smaller, faster start‑uptimes and a truly self‑contained delivery artifact!
## run Maven Goal
The **`run`** goal launches a JavaFX application directly from the Maven build lifecycle.
### When to Use It
- Quick local testing of a JavaFX UI without creating a packaged image.
- Running the application with custom JVM options, debugger attachment, or additional native binaries.
- Integrating UI tests into a CI pipeline that can start the app in head‑less mode (if the test harness supports it).
The goal executes during the `process-classes` phase and requires **runtime** dependency resolution.
### Key Configuration Parameters
| Parameter | Property | Default | Description |
|----------------------|-----------------------------|--------------|----------------------------------------------------------------------------------|
| `executable` | `javafx.executable` | `java` | Path or name of the Java executable used to launch the app. |
| `attachDebugger` | `javafx.attachDebugger` | `false` | When `true`, starts the JVM with JDWP remote debugging enabled. |
| `debuggerPort` | `javafx.debuggerPort` | `5005` | Port on which the debugger listens (used only if `attachDebugger` is `true`). |
| `mainClass` | `javafx.mainClass` | **required** | Fully‑qualified main class (or `module/name` if a module descriptor is present). |
| `loggingFormat` | `javafx.loggingFormat` | – | Overrides `java.util.logging.SimpleFormatter.format` via `-D` system property. |
| `additionalBinaries` | `javafx.additionalBinaries` | – | List of native files to copy and expose as `-D=` system properties. |
| `options` | – | – | Extra JVM options (e.g., `-Xmx1g`). |
| `modulePathElements` | – | – | Resolved module‑path entries (populated by the base mojo). |
| `classPathElements` | – | – | Resolved class‑path entries (populated by the base mojo). |
| `commandlineArgs` | `javafx.args` | – | Arguments passed to the application’s `main` method. |
| `skip` | `javafx.skip` | `false` | Skip execution of the goal entirely. |
| `workingDirectory` | `javafx.workingDirectory` | – | Directory where the process is started; defaults to the project base directory. |
### Typical Usage in `pom.xml`
```xml
io.github.bsels
javafx-maven-plugin
2.0.0
run-javafx
run
process-classes
com.example.app.MainApp
${java.home}/bin/java
true
6006
-Xmx1024m
-Dmy.property=42
--mode demo --verbose
ffmpeg
${project.basedir}/native/ffmpeg.exe
ffmpeg.path
%1$tF %1$tT %4$s %2$s - %5$s%6$s%n
${project.basedir}/target/run-dir
```
### Tips & Gotchas
- **Running on Windows** – Ensure the `executable` points to the correct `java.exe` (or use the default `java`).
- **Debugging** – With `attachDebugger=true`, the JVM will suspend until a debugger attaches. Use `mvnDebug` or connect
a remote debugger to the specified `debuggerPort`.
- **Native Binaries** – The plugin copies each binary into the `bin/` folder of the working directory and adds a
`-D=./` flag, so the application can locate it via `System.getProperty("")`.
- **Large Classpaths** – The generated command may exceed OS limits; consider using the `jlink` goal to create a custom
runtime image for repeated runs.
- **Headless CI** – If the CI environment lacks a display, add `-Djava.awt.headless=true` to options or configure a
virtual frame buffer (e.g., Xvfb on Linux).
- _*Skipping Execution_ – Set `-Djavafx.skip=true` on the command line to bypass the run step (useful for multi‑module
builds where only some modules need to be executed).
---
Add the snippet above to your project's pom.xml and run:
```shell
mvn javafx:run
```
The console will display the exact command executed by the plugin, for example:
```shell
java -Djava.util.logging.SimpleFormatter.format=%1$tF %1$tT %4$s %2$s - %5$s%6$s%n \
-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:*6006 \
-Dffmpeg.path=./ffmpeg.exe \
--enable-native-access=javafx.graphics \
-Xmx1024m -Dmy.property=42 \
-cp target/classes:... \
com.example.app.MainApp --mode demo --verbose
```