0

EDIT #3 (SOLUTION)

I have solved this issue using none of the proposed answers. At the end of this I have some folders and files that I can zip and send around, that do not require a java installation and that can be run by double-clicking a .bat-file. The following procedure worked for me:

  • Create a new gradle project from scratch

  • Add all modules/code into the src/main/java folder

  • create an AppLauncher.java that calls the main-method

  • move all fxml/css from inside one of the java-folders to the resources-folder

  • make sure your calls to fxml-/css-files are not being refactored erroneously and use the following code:

//Main start method
Parent root = FXMLLoader.load(new File("fxml/main.fxml").toURI().toURL());

//Other FXML loading operations
FXMLLoader loader = null;
try { loader = new FXMLLoader(new File("fxml/GRAPHISO_proof.fxml").toURI().toURL()); }
...

and for stylesheets:

scene.getStylesheets().add("file:///" + new File("css/text-field-red-border.css").getAbsolutePath().replace('\\', '/'));
  • create module-info.java inside of src/main/java folder with this structure:
module XYZ {
    requires javafx.controls;
    requires javafx.graphics;
    requires java.desktop;
    requires javafx.fxml;
    requires javafx.base;

    opens main.controller to javafx.fxml;
    //Open ALL controller-modules (all of my modules have a controller-submodule, e.g. main/controller, dlog/controller, graphiso/controller, ...) to javafx.fxml
    //...

    exports main;
}
  • make sure gradle/wrapper/gradle-wrapper.properties is set to a not-so-old version of gradle (distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip)

  • use this as your build.gradle:

buildscript {
    repositories {
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        classpath 'org.openjfx:javafx-plugin:0.0.8'
        classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
    }
}

plugins {
    id 'java'
    id 'org.beryx.jlink' version '2.19.0'
}

apply plugin: 'org.openjfx.javafxplugin'
apply plugin: 'com.github.johnrengelman.shadow'

group 'org.example.abc'
version '1.0'

sourceCompatibility = 1.14

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

javafx {
    sdk = 'C:\\Program Files\\Java\\javafx-sdk-14.0.1'
    modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.base' ]
}

jlink {
    mainClassName = 'main.AppLauncher'
    launcher {
        name = 'Whatever you want as name'
    }
}

tasks.jlink.doLast {
    copy {
        from('src/main/resources')
        into("$buildDir/image/bin")
    }
    copy {
        from 'C:\\Program Files\\Java\\javafx-sdk-14.0.1\\bin\\glass.dll',
             'C:\\Program Files\\Java\\javafx-sdk-14.0.1\\bin\\javafx_font.dll',
             'C:\\Program Files\\Java\\javafx-sdk-14.0.1\\bin\\prism_d3d.dll',
             'C:\\Program Files\\Java\\javafx-sdk-14.0.1\\bin\\prism_sw.dll'
        into("$buildDir/image/bin")
    }
}

Note how I included some dll-files in tasks.jlink.doLast. Without them, the program keeps crashing and refers to the QuantumRenderer.

  • install openjdk-14.0.1 binaries and set your %JAVA_HOME% accordingly, also install openjfx-14.0.1 (program needs some DLLs as mentioned above)

Original Question

I am trying to create a runtime image from a project that I have implemented in JavaFX 11 by just using the "run configuration" functionality in IntelliJ. It seems to me a big problem here is that my knowledge of maven/gradle are almost non-existent and that those build tools are crucial for this step.

Inside of C:\Program Files\Java I've installed:

  • javafx-sdk-11.0.2 and
  • jdk-13.0.2

I configured the VM options inside IntellJ's "run configuration" to be --module-path "C:\Program Files\Java\javafx-sdk-11.0.2\lib" --add-modules javafx.controls,javafx.fxml and set the Main Class to "main.Main".

My project structure looks like this: Project folder structure

The highlighted Main.java is an usual Main-file (extending Application, calling launch(args) inside of main). It can load other windows located in the other packages. It is not a Launcher-file that doesn't extend Application and calls the actual main-file (as suggested by a few solutions for gradle-building).

Initially I've tried to get a .jar-file that can be run without any further parameters on Windows and Linux. After some reading I figured that this wouldn't be a clean solution and a lot of sources stated that in modern solutions the .jar-files will be put together and shipped with a .bat/.sh file that includes all necessary parameters.

My question is: How do I best proceed and which configurations/additional files do I need to transform this "click-to-run-inside-IDE" project into an "standalone-application" that I can share through e-mail or similar?

I have tried to create a new gradle project through IntelliJ (following https://openjfx.io/openjfx-docs) and copy-pasted the source files into src/main/java. I then tried out the provided build.gradle-file and - after that didn't work - made some changes suggested by various blogs/forums/etc. None of them allowed me to run the project through IntelliJ or from the command line.

Most of these approaches let me build the project but when trying to run using either "gradlew run" or calling the "java -jar file.jar" I got error messages due to class exceptions or saying I am still missing the javafx modules. I've also tried an approach to convert my current project into a gradle-project but that wouldn't even let me build the project afterwards.


EDIT #1 (JPackageScriptFX)

I have looked at the JPackageScriptFX-link. Now I am getting an installer (using mvn clean install) that works on my machine, but when running the resulting executable, I receive an error message that says "Failed to launch JVM".

What I have done so far is:

  • installed jdk-14.0.1 and set JAVA_HOME, installed wix311

  • created a new IntelliJ maven project called "bachelor_maven"

  • removed initial src-folder

  • added new module "ZKP_Inspector" and my old modules inside of the resulting src/main/java folder

  • added and edited src/main/logo/windows/duke.ico, the two pom.xml files and build_app.bat from JPackageScriptFX

  • implemented AppLauncher.java that calls Main.java

My configuration files:

  • New project structure: Imgur

  • pom.xml (Project level, bachelor_maven): Pastebin

  • pom.xml (module level, ZKP_Inspector): Pastebin

  • build_app.bat: Pastebin


EDIT #2

I tried removing --runtime-image target/java-runtime ^ from the jpackage parameters in the build_app.bat file as to let jpackage decide what to include. This increased the file size of the installer (as expected) but lead to the same error when trying to execute the resulting program.

I have also downloaded the JMOD files and used them in the jlink task in the build_app.bat as hinted here.

So I replaced --add-modules %detected_modules%,%manual_modules% ^ with

--module-path "C:\Program Files\Java\javafx-jmods-11.0.2" --add-modules %detected_modules%,%manual_modules%,javafx.controls,javafx.fxml

This produced another installer that ran fine and whose resulting program threw the same error.

TMS
  • 1
  • 2
  • Please see https://github.com/dlemmermann/JPackageScriptFX – CrazyCoder May 27 '20 at 23:10
  • please see this Post for Gradle and JFX 11+ : https://stackoverflow.com/questions/60893053/javafx-10-or-older-for-download-to-create-a-runnable-jar-file/60893551?noredirect=1#comment107758642_60893551 – Luxusproblem May 28 '20 at 07:04
  • I could help you, but I am using Maven for my case. – Alexiy May 28 '20 at 14:03
  • @CrazyCoder Please see my edited question. I have tried to solve it with Maven and JPackageScriptFX. – TMS May 28 '20 at 16:12
  • @Alexiy Well, I've now switched to using a maven project to try out the solution proposed in JPackageScriptFX. – TMS May 28 '20 at 16:13

3 Answers3

3

Alright, I will try to show how to achieve this with jlink only. Suppose we have an example project in a directory 'C:\Users\Alexiy\IdeaProjects\AnotherBuildTool\rundir'. Its structure is:

/rundir
--classes [compilation output]
--source  [source code]
--images  [runtime image output]

I have a class 'source\io\github\alexiyorlov\calculator\Program.java':

package io.github.alexiyorlov.calculator;

import javafx.application.Application;
import javafx.stage.Stage;
import java.nio.file.Path;
public class Program extends Application {

   @Override
   public void start(Stage primaryStage) throws Exception {
       System.out.println("I'm a simple application");
       primaryStage.show();
   }
}

And an appropriate 'source\module-info.java':

module io.github.alexiyorlov{
   requires javafx.controls;
   requires javafx.graphics;
   exports io.github.alexiyorlov.calculator;
}

If I want to make a runtime image out of this, the syntax of jlink command will be like this:

jlink --launcher [name for the launch script]=[reference to the main class] --module-path '[separated list of project's dependency jars + compile output directory]' --add-modules [my module reference] --compress=2 --output [directory for the resulting runtime image]

So, for my example project it resolves into this command:

jlink 
--launcher launch=io.github.alexiyorlov/io.github.alexiyorlov.calculator.Program 
--module-path 'C:\Users\Alexiy\.m2\repository\org\openjfx\javafx-controls\11.0.2\javafx-controls-11.0.2-win.jar;C:\Users\Alexiy\.m2\repository\org\junit\jupiter\junit-jupiter-engine\5.7.0-M1\junit-jupiter-engine-5.7.0-M1.jar;C:\Users\Alexiy\.m2\repository\org\openjfx\javafx-base\11.0.2\javafx-base-11.0.2.jar;C:\Users\Alexiy\.m2\repository\org\openjfx\javafx-base\11.0.2\javafx-base-11.0.2-win.jar;C:\Users\Alexiy\.m2\repository\org\openjfx\javafx-graphics\11.0.2\javafx-graphics-11.0.2.jar;C:\Users\Alexiy\.m2\repository\dev\buildtool\json-tools\0.1.0\json-tools-0.1.0.jar;C:\Users\Alexiy\IdeaProjects\AnotherBuildTool\rundir\org\openjfx\javafx\11.0.2\javafx-11.0.2/.jar;C:\Users\Alexiy\.m2\repository\org\openjfx\javafx-controls\11.0.2\javafx-controls-11.0.2.jar;C:\Users\Alexiy\.m2\repository\org\openjfx\javafx-graphics\11.0.2\javafx-graphics-11.0.2-win.jar;C:\Users\Alexiy\IdeaProjects\AnotherBuildTool\rundir/classes' 
--add-modules io.github.alexiyorlov 
--compress=2 
--output .\images\0.0.1

It will create an image in a directory 'images\0.0.1' with start scripts '0.0.1\bin\launch'.

Note that I used an automated way to get the right argument for --module-path; be sure to include class output directory in it.

--add-modules specifies which modules of your application to include.

--output specifies a directory to put all files of the image.

--launcher specifies the name of the launch scripts and the correct reference to the main class after '='.

--compress=2 makes it to apply a ZIP compression to the image.

Feel free to request more info from me.

Alexiy
  • 1,966
  • 16
  • 18
1

I have had this same issue for some time now; it is considerably problematic that IntelliJ can't deploy an FX application on its own.

The first step I would recommend, if you have not already done so, is creating a standard Jar file and running it from the command line along with your required additional arguments:

java --module-path "C:\Program Files\Java\javafx-sdk-11.0.2\lib" --add-modules javafx.controls,javafx.fxml -jar your-jar.jar

This is simply to verify that it starts and runs. I've learned that simply because it runs in the IDE, does not mean errors won't pop up when running the JAR - it's best to verify that first off.

Then, there are a number of ways you can achieve what you desire, all with varying amounts of simplicity and neatness.

  1. As you've said, you can run a .bat file to launch it. This is the simplest method.
  2. You can create a new Windows shortcut for it; in the target destination field, place your command that you would have placed in the bat file, but include the full file path to javaw.exe and the full path to your jar file. This solution is more elegant than the above, but still requires two files (your jar and the shortcut)
  3. You can try using an application called Launch4j. It packages the jar and only the necessary components of your JVM into an exe file, and you can specify the VM arguments there as well. This has the added benefit of the client not requiring a specific version of Java, as you essentially include it in your file.
  4. If you truly want to package only the jar file, this is the most complicated way, but it is possible. I've had success with the following technique:

First, compile the JavaFX jar as normal, hereafter referenced fx.jar. Then, create a non-JavaFX project, hereafter referenced nonfx.jar. Place fx.jar in the src folder of nonfx. When nonfx runs, it should extract fx.jar from its own archive, then run a command that opens fx.jar with the necessary VM arguments. Essentially, nonfx is the container for fx.jar. In order to delete fx.jar when it has closed, I'd recommend creating a Windows Task that does so from within fx.jar.

This method is fairly tiresome and error-prone, but it does work. There are probably better ways to do this with Gradle. Until fx:deploy is included in IntelliJ again, workarounds are the best we have.

0

I have solved this issue using an almost self-built solution now. At the end of this I have some folders and files that I can zip and send around, that do not require a java installation and that can be run by double-clicking a .bat-file. The following procedure worked for me:

  • Create a new gradle project from scratch

  • Add all modules/code into the src/main/java folder

  • create an AppLauncher.java that calls the main-method

  • move all fxml/css from inside one of the java-folders to the resources-folder

  • make sure your calls to fxml-/css-files are not being refactored erroneously and use the following code:

//Main start method
Parent root = FXMLLoader.load(new File("fxml/main.fxml").toURI().toURL());

//Other FXML loading operations
FXMLLoader loader = null;
try { loader = new FXMLLoader(new File("fxml/GRAPHISO_proof.fxml").toURI().toURL()); }
...

and for stylesheets:

scene.getStylesheets().add("file:///" + new File("css/text-field-red-border.css").getAbsolutePath().replace('\\', '/'));
  • create module-info.java inside of src/main/java folder with this structure:
module XYZ {
    requires javafx.controls;
    requires javafx.graphics;
    requires java.desktop;
    requires javafx.fxml;
    requires javafx.base;

    opens main.controller to javafx.fxml;
    //Open ALL controller-modules (all of my modules have a controller-submodule, e.g. main/controller, dlog/controller, graphiso/controller, ...) to javafx.fxml
    //...

    exports main;
}
  • make sure gradle/wrapper/gradle-wrapper.properties is set to a not-so-old version of gradle (distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip)

  • use this as your build.gradle:

buildscript {
    repositories {
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        classpath 'org.openjfx:javafx-plugin:0.0.8'
        classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
    }
}

plugins {
    id 'java'
    id 'org.beryx.jlink' version '2.19.0'
}

apply plugin: 'org.openjfx.javafxplugin'
apply plugin: 'com.github.johnrengelman.shadow'

group 'org.example.abc'
version '1.0'

sourceCompatibility = 1.14

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

javafx {
    sdk = 'C:\\Program Files\\Java\\javafx-sdk-14.0.1'
    modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.base' ]
}

jlink {
    mainClassName = 'main.AppLauncher'
    launcher {
        name = 'Whatever you want as name'
    }
}

tasks.jlink.doLast {
    copy {
        from('src/main/resources')
        into("$buildDir/image/bin")
    }
    copy {
        from 'C:\\Program Files\\Java\\javafx-sdk-14.0.1\\bin\\glass.dll',
             'C:\\Program Files\\Java\\javafx-sdk-14.0.1\\bin\\javafx_font.dll',
             'C:\\Program Files\\Java\\javafx-sdk-14.0.1\\bin\\prism_d3d.dll',
             'C:\\Program Files\\Java\\javafx-sdk-14.0.1\\bin\\prism_sw.dll'
        into("$buildDir/image/bin")
    }
}

Note how I included some dll-files in tasks.jlink.doLast. Without them, the program keeps crashing and refers to the QuantumRenderer.

  • install openjdk-14.0.1 binaries and set your %JAVA_HOME% accordingly, also install openjfx-14.0.1 (program needs some DLLs as mentioned above)
TMS
  • 1
  • 2