Search code examples
javaspring-bootjavafxjpackage

JPackaged JavaFX + Spring boot does not launch


I am trying to build a JavaFX application using Spring Boot and deploy it with jpackage.

When using the javafx-maven-plugin javafx:run command, I can see project launching. But after building it to an *.msi installer, installing and launching the *.exe, nothing happens (whether by double-click or command line run).

How can I fix this, or at least know what is happening? I suspect the javafx:run goal to have some parameter that is missing from the manual jlink, however I can't use javafx:jlink as the application itself is not a module, because Spring Boot is not modularized yet.

Here are the sources;

EDIT: On @Slaw's suggestion, I modified the jpackage command to include `--win-console. Which successfully runs the app. Although a great step, that is not what I'm looking for, I would like to run it without having a prompt beside it.

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.test</groupId>
    <artifactId>mytest</artifactId>
    <packaging>jar</packaging>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.6.0</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.6.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>17.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-graphics</artifactId>
            <version>17.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>17.0.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.6.0</version>
            </plugin>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.8</version>
                <configuration>
                    <mainClass>org.test.Main</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>install</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Main:

package org.test;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Main extends Application {
    private ConfigurableApplicationContext springContext;
    private Parent rootNode = new AnchorPane();
    public static void main(String[] args) {
        Application.launch(args);
    }
    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(rootNode, 700, 700));
        stage.setMinWidth(700);
        stage.setMinHeight(700);
        stage.show();
    }
    public void init() throws  Exception{
        springContext = SpringApplication.run(Main.class);
    }
    public void stop() throws Exception{
        springContext.close();
    }
}

And my build script is:

@ECHO OFF
SETLOCAL
call mvn clean install
set manual_modules=,javafx.controls,javafx.graphics,javafx.fxml
for /f %%i in ('jdeps --multi-release 17 --ignore-missing-deps --print-module-deps --class-path "target/lib/*" target/classes/org/test/Main.class') do set detected_modules=%%i
echo jlink
jlink --no-header-files --no-man-pages --compress=2 --strip-debug --module-path "target/lib" --add-modules %detected_modules%%manual_modules% --output target/java-runtime
echo jpackage
jpackage --type msi --dest target/installer --input target --name mytest --main-class org.test.Main --main-jar mytest-1.0.jar --runtime-image target/java-runtime
endlocal
@ECHO ON

Solution

  • I have no explanation as though why the first jpackage command works with --win-console and not without. Something I do not understand must be happening under the hood (I'm still interested in that answer if anyone knows!).

    Anyways, I managed to produce an exe after some debugging and many attempts.

    1. Launching the application via the Spring Boot Loader, meaning the jpackage command is now: jpackage --type msi --dest output/installer --input target --name testing --main-class org.springframework.boot.loader.PropertiesLauncher --main-jar mytest-1.0.jar --runtime-image target/java-runtime
    2. Add spring boot loader dependency in pom.xml
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-loader</artifactId>
        <version>2.6.0</version>
    </dependency>
    
    1. Modify the spring-boot-maven-plugin configuration:
               <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>2.6.0</version>
                    <configuration>
                        <mainClass>org.test.Main</mainClass>
                        <layout>ZIP</layout>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
    

    I admit what I'm doing here is quite experimental (at least to me), all answers that could provide a more in-depth look, references or a better way would be welcomed.