4

Currently I am using a JVM argument when launching my program for it to find the JavaFX libraries like so :

java -javaagent:lib/aspectjweaver-1.9.5.jar -javaagent:lib/spring-instrument-5.2.3.RELEASE.jar --module-path lib/javafx-sdk-13.0.2/lib --add-modules=javafx.controls -jar target/Some_Service-1.0.jar

My POM.xml's plugins section is extremely simple. Aside from the Docker and Launch4j plugins I only have this :

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Is there a way to tell Maven to bundle that whole lib directory (which contains the JavaFX libraries and aspectJ/spring instrument java agents) right in the jar? Doing this would solve the issue of having to carry that lib folder around everywhere I deploy my app! Thanks!

******EDIT******

I messed around with the spring boot maven plugin options and unfortunately my jar still doesn't contain my folder :

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <folders>
            <folder>lib</folder>
        </folders>
        <agent>lib/aspectjweaver-1.9.5.jar</agent>
        <agent>lib/spring-instrument-5.2.3.RELEASE.jar</agent>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>

******EDIT 2******

I just found an awesome library that eliminates the need for the -javaagent parameters altogether by having you load them programmatically at runtime instead. It looks like the very presence of AspectJ and Spring-Instrument on my classpath is all it needs too! Check it out : https://github.com/subes/invesdwin-instrument.

All I need now is to somehow bundle the JavaFX runtime into my jar instead of referring to it externally using a command line argument.

Martin
  • 1,977
  • 5
  • 30
  • 67
  • try https://stackoverflow.com/questions/97640/force-maven-to-copy-dependencies-into-target-lib – Sergey Grinev Feb 20 '20 at 05:04
  • problem is, this is not a project dependency. Those are external files that maven has no idea about and are only provided at runtime. – Martin Feb 20 '20 at 13:41
  • What files are missing exactly? – hooknc Feb 20 '20 at 21:25
  • my lib folder contains the AspectJ weaver jar, Spring Instrument jar and javaFx librairies which are no longer bundled in the main Java runtimes (running Java 13). Those 2 first files need to be specified as java agents in the java -jar command, the javaFX files also need to be specified as you see in my java -jar command above. – Martin Feb 20 '20 at 21:38
  • I just noticed something, the 2 first libraries are already in the jar under the BOOT-INF\lib directory. How would I refer to those internal ones as java agents? – Martin Feb 20 '20 at 21:41
  • Can you find the jars you need in the [maven central](https://mvnrepository.com/repos/central) repository? Because if you add those jars as dependencies, the they should be packaged durning `repackage`. I have been looking at how to define agents with the [spring-boot-maven-plugin](https://docs.spring.io/spring-boot/docs/current/maven-plugin/usage.html), but I haven't exactly figured it out yet. – hooknc Feb 21 '20 at 00:13
  • check the edit I just did! You're welcome :P – Martin Feb 21 '20 at 00:16
  • 1
    to answer your first question, those files are not provided by Maven as dependencies, they are provided separated from openJDK (openJFX). Those files used to be bundled directly in the JRE but are no longer since Java 11, therefore they have to be referred to this way. – Martin Feb 21 '20 at 01:36
  • https://maven.apache.org/plugins/maven-shade-plugin/ – Slaw Feb 24 '20 at 17:13
  • doesn't the shade plugin only work with Maven dependencies? – Martin Feb 24 '20 at 18:11
  • JavaFX is a Maven dependency. Or, at least, you can add the dependencies to your `pom.xml` and they'll be downloaded from Maven Central. Unlike the SDK, the JARs from the repository embed the native code. Note that your fat/uber JAR will be platform-specific (without [additional work](https://stackoverflow.com/a/52654791/6395627)). May also want to look into the [javafx-maven-plugin](https://github.com/openjfx/javafx-maven-plugin). – Slaw Feb 25 '20 at 11:42
  • unless you know something I don't (and I hope you do!), the SDK is REQUIRED for a JavaFX app to work. I have the JavaFX dependencies in my POM.xml but the app throws an error saying it can't find the JavaFX components unless I specifically add --module-path lib/javafx-sdk-13.0.2/lib --add-modules=javafx.controls in the command line arguments. Are you saying this is not needed?! – Martin Feb 25 '20 at 13:25
  • 1
    The SDK is not required. The artifacts in Maven Central contain all the code you need (including platform-specific native code, which gets extracted at runtime). That error you're getting can also occur if your main class is your `Application` implementation and the `javafx.graphics` modules is being used from the _classpath_. If using everything from the classpath (which is likely with a fat jar) then your main class _cannot_ be a subclass of `Application`. See [this answer](https://stackoverflow.com/a/52571719/6395627) (especially the edit) and the answer I linked to in my previous comment. – Slaw Feb 25 '20 at 13:54
  • I will try that for sure! – Martin Feb 25 '20 at 14:11
  • YOU ARE THE BEST! Please post an answer my friend! – Martin Feb 25 '20 at 14:24

1 Answers1

5

Warning: Since a fat/uber JAR will likely cause JavaFX to be placed on the class-path, note this approach is not supported.

Instead of the using the JavaFX SDK, declare your dependence on JavaFX in your pom.xml file. Then you can use the maven-shade-plugin to create an executable fat/uber JAR. The appropriate JavaFX JARs (e.g. the graphics JAR file) downloaded from Maven Central embed the needed, platform-specific native code which will be extracted at runtime (e.g. to the user home directory).

Here's a minimal example:

Launcher.java:

package com.example;

import javafx.application.Application;

public class Launcher {

  public static void main(String[] args) {
    Application.launch(App.class, args);
  }
}

App.java:

package com.example;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class App extends Application {

  @Override
  public void start(Stage primaryStage) {
    StackPane root = new StackPane(new Label("Hello, World!"));
    primaryStage.setScene(new Scene(root, 500, 300));
    primaryStage.show();
  }
}

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>javafx-uber-jar</artifactId>
    <version>1.0</version>

    <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <maven.compiler.source>13</maven.compiler.source>
      <maven.compiler.target>13</maven.compiler.target>
    </properties>

    <build>
      <plugins>

        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.1</version>
          <configuration>
              <release>13</release>
          </configuration>
        </plugin>

        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-shade-plugin</artifactId>
          <version>3.2.2</version>
          <executions>
            <execution>
              <phase>package</phase>
              <goals>
                <goal>shade</goal>
              </goals>
              <configuration>
                <transformers>
                  <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                    <mainClass>com.example.Launcher</mainClass>
                  </transformer>
                </transformers>
              </configuration>
            </execution>
          </executions>
        </plugin>

      </plugins>
    </build>

    <dependencies>
      <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>13.0.2</version>
      </dependency>
    </dependencies>

</project>

Command line:

mvn package

The above will create a platform-specific uber JAR. See this answer for ideas on how to create a cross-platform uber JAR.

Depending on the version of Java you're using to develop your application, you could look into jlink (Java 9+) and/or jpackage (Java 14, early-access) as alternatives to creating a fat/uber JAR file. If you use one of those, consider using the JMOD files of JavaFX (can be found here) as it configures the custom image to handle native code better (i.e. no extraction).


Notice that the main class is not a subclass of Application. This is mandatory for applications which put the JavaFX libraries on the classpath, at least for JavaFX 9-13. The reason for this requirement has to do with the implementation allowing JavaFX applications to not have a main method; if the javafx.graphics module is not on the modulepath it is assumed that "JavaFX components are missing". But that only occurs when the main class and Application class are the same, hence the above workaround.

Slaw
  • 37,820
  • 8
  • 53
  • 80
  • You have no idea how long I've searched for this and tried everything that any doc said. But the fact that you need to separate the Main class for the App is the one thing that fixed it all and that isn't told anywhere. Thank you for that and get my upvote :) – lolgab123 Feb 12 '22 at 20:04
  • I just second @lolgab123, I was searching for this issue for a long time, you are the first one who mentioned about the separation of the main class. Thank you for your time and effort for creating this answer. You literally made rest of my Sunday – Hizir Apr 24 '22 at 17:39