2

My target is to create an application, which uses Java 21.0.7 (OpenJDK), JavaFx 23.0.2 and GeoTools 34-SNAPSHOT to show S57 sea charts.

As a prerequirement I have downloaded javafx-sdk-23.0.2 as well and set the environment variable FX-PATH=C:\Git\openjfx-23.0.2_windows-x64_bin-sdk\javafx-sdk-23.0.2\lib.

The application contains the pom.xml and two classes:

    <?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.example</groupId>
    <artifactId>GeoToolsTest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <version.java>21</version.java>
        <maven.compiler.source>${version.java}</maven.compiler.source>
        <maven.compiler.target>${version.java}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <version.javafx>23.0.2</version.javafx>
        <version.geotools>34-SNAPSHOT</version.geotools>
        <maven.deploy.skip>true</maven.deploy.skip>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- Import the GeoTools BOM -->
            <dependency>
                <groupId>org.geotools</groupId>
                <artifactId>gt-bom</artifactId>
                <version>${version.geotools}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <repositories>
        <repository>
            <id>osgeo</id>
            <name>OSGeo Release Repository</name>
            <url>https://repo.osgeo.org/repository/release/</url>
            <snapshots><enabled>false</enabled></snapshots>
            <releases><enabled>true</enabled></releases>
        </repository>
        <repository>
            <id>osgeo-snapshot</id>
            <name>OSGeo Snapshot Repository</name>
            <url>https://repo.osgeo.org/repository/snapshot/</url>
            <snapshots><enabled>true</enabled></snapshots>
            <releases><enabled>false</enabled></releases>
        </repository>
    </repositories>

    <build>
        <resources>
            <resource>
                <directory>src/main/config</directory>
                <targetPath>${project.build.directory}</targetPath>
                <filtering>true</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.14.0</version>
                <configuration><release>${version.java}</release></configuration>
            </plugin>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.8</version>
                <configuration><mainClass>org.example.Main</mainClass></configuration>
            </plugin>
            <plugin>
                <!-- Build an executable JAR -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.4.2</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>org.example.Starter</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx</artifactId>
            <version>${version.javafx}</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${version.javafx}</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-swing</artifactId>
            <version>${version.javafx}</version>
        </dependency>
       <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-shapefile</artifactId>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-swing</artifactId>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-api</artifactId>
        </dependency>
    </dependencies>

</project>

The class org.example.Starter (which acts as a wrapper class):

package org.example;
public class Starter {
    //Wrapper class
    public static void main(String[] args) {
        Main.main(args);
    }
}

And the file: org.example.Main:

package org.example;

import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import org.geotools.api.data.DataStore;
import org.geotools.api.data.DataStoreFinder;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.swing.JMapPane;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("S-57 seachart with GeoTools");
        SwingNode swingNode = new SwingNode();
        createAndSetMapPane(swingNode);

        BorderPane root = new BorderPane();
        root.setCenter(swingNode);

        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void createAndSetMapPane(SwingNode swingNode) {
        javafx.application.Platform.runLater(() -> {
              MapContent mapContent = new MapContent();
              mapContent.setTitle("S-57 Sea chart");

            try {
                File s57File = new File("PathToS57Files\\file.000");
                Map<String, Object> params = new HashMap<>();
                params.put("url", s57File.toURI().toURL());

                DataStore dataStore = DataStoreFinder.getDataStore(params);
                String typeName = dataStore.getTypeNames()[0];

                Layer layer = new org.geotools.map.FeatureLayer(dataStore.getFeatureSource(typeName), null);
                mapContent.addLayer(layer);

                JMapPane mapPane = new JMapPane();
                mapPane.setMapContent(mapContent);

                swingNode.setContent(mapPane);

            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public static void main(String[] args) {
        launch(args);
    }
}

I start my application with the following command:

java --module-path %FX-PATH% --add-modules javafx.controls,javafx.swing -jar GeoToolsTest-1.0-SNAPSHOT.jar Main

but it ends up with the following exception:

    Exception in thread "main" java.lang.NoClassDefFoundError:  org/geotools/api/data/FeatureSource
        at org.example.Starter.main(Starter.java:7)
Caused by: java.lang.ClassNotFoundException: org.geotools.api.data.FeatureSource
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
        ... 1 more

Could someone explain, what is missing or wrong?

Some notices:

  • As far as i can see the class FeatureSource is part of org.geotools.gt-api and this dependency is included.

  • Even if i remove the gt-bom-pattern (which is introduced by version 34-SNAPSHOT) and set all geotools-dependencies to the last stable version 33.2 this exception appears.

2
  • Don't you need to include the geotools jars on the class path? Commented Aug 5 at 21:35
  • --add-modules javafx.controls,javafx.swing ← You didn’t add a module containing the geotools api classes, so it makes sense that the program cannot find them at runtime. Commented Aug 6 at 18:45

1 Answer 1

3

Why it breaks

Your main issues are:

  1. The geotools 34-SNAPSHOT (seemingly) breaks the manifest of the packaged jar file.
  2. You don't have the required /lib directory with the needed libraries in it.

How to fix it

Replace the geotools snapshot dependency with a stable version

I could not tell you exactly why the geotools snapshot dependencies breaks your execution. My guess is that the snapshot artifact library names have some characters or lengths that are incompatible with the jar class path manifest specification. Regardless, using the stable version of geotools removed the issue.

To switch from the geotools snapshot to the stable version:

  • Remove the geotools snapshot repository definition.
  • Remove the geotools 34-SNAPSHOT bom plugin management section.
  • Add dependency versions for geotools yourself manually, rather than relying on the snapshot bom which breaks your deployment.

Place the required libraries in the /lib directory

You have these options for the maven-jar-plugin:

<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>

This means that all of the jars listed in the META-INF/MANIFEST.MF file in the jar you are executing need to be in the relative lib/ directory to the executed jar. The relative lib directory needs to include the transitive jars of Geotools and JavaFX, based on your current configuration.

You can have Maven copy the required libraries to the project target/lib directory using this configuration.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>3.8.1</version>
    <executions>
        <execution>
            <phase>prepare-package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/lib</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

Example working code

I couldn't get an an actual chart to display as I didn't have the data file referenced in your question "PathToS57Files\\file.000". However, I think it works otherwise (for me it just displays a blank screen where the chart should be).

I renamed some of the classes as per the info in the advice section of this answer. I also commented out bits of code related to missing data.

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>geotools-test</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>geotools-test</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <java.version>21</java.version>

        <geotools.version>33.2</geotools.version>
        <javafx.version>23.0.2</javafx.version>
    </properties>

    <repositories>
        <repository>
            <id>osgeo</id>
            <name>OSGeo Release Repository</name>
            <url>https://repo.osgeo.org/repository/release/</url>
            <snapshots><enabled>false</enabled></snapshots>
            <releases><enabled>true</enabled></releases>
        </repository>
    </repositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.14.0</version>
                <configuration>
                    <release>${java.version}</release>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.8.1</version>
                <executions>
                    <execution>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <!-- Build an executable JAR -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.4.2</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>org.example.SeaChartLauncher</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-swing</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-shapefile</artifactId>
            <version>${geotools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-swing</artifactId>
            <version>${geotools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-api</artifactId>
            <version>${geotools.version}</version>
        </dependency>
    </dependencies>
</project>
package org.example;

public class SeaChartLauncher {
    public static void main(String[] args) {
        SeaChartApp.main(args);
    }
}
package org.example;

import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import org.geotools.api.data.DataStore;
import org.geotools.api.data.DataStoreFinder;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.swing.JMapPane;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class SeaChartApp extends Application {

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("S-57 seachart with GeoTools");
        SwingNode swingNode = new SwingNode();
        createAndSetMapPane(swingNode);

        BorderPane root = new BorderPane();
        root.setCenter(swingNode);

        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void createAndSetMapPane(SwingNode swingNode) {
        javafx.application.Platform.runLater(() -> {
              MapContent mapContent = new MapContent();
              mapContent.setTitle("S-57 Sea chart");

            try {
//                File s57File = new File("PathToS57Files\\file.000");
//                Map<String, Object> params = new HashMap<>();
//                params.put("url", s57File.toURI().toURL());
//
//                DataStore dataStore = DataStoreFinder.getDataStore(params);
//                String typeName = dataStore.getTypeNames()[0];
//
//                Layer layer = new org.geotools.map.FeatureLayer(dataStore.getFeatureSource(typeName), null);
//                mapContent.addLayer(layer);

                JMapPane mapPane = new JMapPane();
                mapPane.setMapContent(mapContent);

                swingNode.setContent(mapPane);

            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Build command

mvn clean package

Execution command

java -jar ./target/geotools-test-1.0-SNAPSHOT.jar

Files created

I was using a Mac (Intel) PC for the build and execution, so that is the version of the JavaFX libraries in the lib directory. I used JavaFX 23.0.2 and OpenJDK 21.0.8, on OS X (Intel) 15.5.

app jar file

This jar file references files in the relative lib/ directory in its manifest (shown below).

$ ls target/geotools-test-1.0-SNAPSHOT.jar 
target/geotools-test-1.0-SNAPSHOT.jar

lib directory contents

$ ls target/lib
aircompressor-0.27.jar
apiguardian-api-1.1.2.jar
bigint-0.7.1.jar
checker-qual-3.43.0.jar
commons-io-2.19.0.jar
commons-lang3-3.17.0.jar
commons-pool-1.5.4.jar
commons-text-1.13.0.jar
disruptor-1.2.15.jar
ejml-core-0.41.jar
ejml-ddense-0.41.jar
error_prone_annotations-2.36.0.jar
failureaccess-1.0.2.jar
FastInfoset-1.2.15.jar
GeographicLib-Java-1.49.jar
gt-api-33.2.jar
gt-coverage-33.2.jar
gt-cql-33.2.jar
gt-http-33.2.jar
gt-main-33.2.jar
gt-metadata-33.2.jar
gt-referencing-33.2.jar
gt-render-33.2.jar
gt-shapefile-33.2.jar
gt-swing-33.2.jar
guava-33.4.0-jre.jar
imageio-ext-geocore-1.4.15.jar
imageio-ext-streams-1.4.15.jar
imageio-ext-tiff-1.4.15.jar
imageio-ext-utilities-1.4.15.jar
indriya-2.2.jar
istack-commons-runtime-3.0.7.jar
j2objc-annotations-3.0.0.jar
jackson-core-2.19.0.jar
jai_codec-1.1.3.jar
jai_core-1.1.3.jar
jai_imageio-1.1.jar
jakarta.annotation-api-1.3.4.jar
jakarta.inject-api-2.0.1.jar
javafx-base-23.0.2-mac.jar
javafx-base-23.0.2.jar
javafx-controls-23.0.2-mac.jar
javafx-controls-23.0.2.jar
javafx-graphics-23.0.2-mac.jar
javafx-graphics-23.0.2.jar
javafx-swing-23.0.2-mac.jar
javafx-swing-23.0.2.jar
javax.activation-api-1.2.0.jar
jaxb-api-2.4.0-b180830.0359.jar
jaxb-runtime-2.4.0-b180830.0438.jar
jgridshift-core-1.3.jar
jsr305-3.0.2.jar
jt-affine-1.1.31.jar
jt-algebra-1.1.31.jar
jt-bandcombine-1.1.31.jar
jt-bandmerge-1.1.31.jar
jt-bandselect-1.1.31.jar
jt-binarize-1.1.31.jar
jt-border-1.1.31.jar
jt-buffer-1.1.31.jar
jt-classifier-1.1.31.jar
jt-colorconvert-1.1.31.jar
jt-colorindexer-1.1.31.jar
jt-crop-1.1.31.jar
jt-errordiffusion-1.1.31.jar
jt-format-1.1.31.jar
jt-imagefunction-1.1.31.jar
jt-iterators-1.1.31.jar
jt-lookup-1.1.31.jar
jt-mosaic-1.1.31.jar
jt-nullop-1.1.31.jar
jt-orderdither-1.1.31.jar
jt-piecewise-1.1.31.jar
jt-rescale-1.1.31.jar
jt-rlookup-1.1.31.jar
jt-scale-1.1.31.jar
jt-scale2-1.1.31.jar
jt-shadedrelief-1.1.31.jar
jt-stats-1.1.31.jar
jt-translate-1.1.31.jar
jt-utilities-1.1.31.jar
jt-utils-1.6.0.jar
jt-vectorbin-1.1.31.jar
jt-warp-1.1.31.jar
jt-zonal-1.1.31.jar
jt-zonalstats-1.6.0.jar
jts-core-1.20.0.jar
listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar
miglayout-3.7-swing.jar
net.opengis.ows-33.2.jar
org.eclipse.emf.common-2.15.0.jar
org.eclipse.emf.ecore-2.15.0.jar
org.eclipse.emf.ecore.xmi-2.15.0.jar
org.w3.xlink-33.2.jar
re2j-1.8.jar
si-quantity-2.1.jar
si-units-2.1.jar
slf4j-api-1.7.13.jar
stax-ex-1.8.jar
systems-common-2.1.jar
txw2-2.4.0-b180830.0438.jar
unit-api-2.2.jar
uom-lib-common-2.2.jar

Generated manifest file

Content of META-INF/MANIFEST.MF in the target/geotools-test-1.0-SNAPSHOT.jar file:

Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.4.2
Build-Jdk-Spec: 21
Class-Path: lib/javafx-controls-23.0.2.jar lib/javafx-controls-23.0.2-ma
 c.jar lib/javafx-graphics-23.0.2.jar lib/javafx-graphics-23.0.2-mac.jar
  lib/javafx-base-23.0.2.jar lib/javafx-base-23.0.2-mac.jar lib/javafx-s
 wing-23.0.2.jar lib/javafx-swing-23.0.2-mac.jar lib/gt-shapefile-33.2.j
 ar lib/gt-main-33.2.jar lib/gt-http-33.2.jar lib/commons-text-1.13.0.ja
 r lib/commons-lang3-3.17.0.jar lib/jackson-core-2.19.0.jar lib/re2j-1.8
 .jar lib/ejml-ddense-0.41.jar lib/ejml-core-0.41.jar lib/commons-io-2.1
 9.0.jar lib/jai_core-1.1.3.jar lib/gt-swing-33.2.jar lib/gt-referencing
 -33.2.jar lib/gt-metadata-33.2.jar lib/net.opengis.ows-33.2.jar lib/org
 .w3.xlink-33.2.jar lib/org.eclipse.emf.common-2.15.0.jar lib/org.eclips
 e.emf.ecore-2.15.0.jar lib/org.eclipse.emf.ecore.xmi-2.15.0.jar lib/jgr
 idshift-core-1.3.jar lib/GeographicLib-Java-1.49.jar lib/gt-render-33.2
 .jar lib/gt-coverage-33.2.jar lib/jai_imageio-1.1.jar lib/imageio-ext-t
 iff-1.4.15.jar lib/imageio-ext-utilities-1.4.15.jar lib/imageio-ext-geo
 core-1.4.15.jar lib/imageio-ext-streams-1.4.15.jar lib/jaxb-api-2.4.0-b
 180830.0359.jar lib/jaxb-runtime-2.4.0-b180830.0438.jar lib/txw2-2.4.0-
 b180830.0438.jar lib/istack-commons-runtime-3.0.7.jar lib/stax-ex-1.8.j
 ar lib/FastInfoset-1.2.15.jar lib/javax.activation-api-1.2.0.jar lib/ai
 rcompressor-0.27.jar lib/jai_codec-1.1.3.jar lib/jt-zonalstats-1.6.0.ja
 r lib/jt-utils-1.6.0.jar lib/jt-affine-1.1.31.jar lib/jt-algebra-1.1.31
 .jar lib/jt-bandmerge-1.1.31.jar lib/jt-bandselect-1.1.31.jar lib/jt-ba
 ndcombine-1.1.31.jar lib/jt-border-1.1.31.jar lib/jt-buffer-1.1.31.jar 
 lib/jt-crop-1.1.31.jar lib/jt-iterators-1.1.31.jar lib/jt-lookup-1.1.31
 .jar lib/jt-mosaic-1.1.31.jar lib/jt-nullop-1.1.31.jar lib/jt-rescale-1
 .1.31.jar lib/jt-scale-1.1.31.jar lib/jt-scale2-1.1.31.jar lib/bigint-0
 .7.1.jar lib/jt-stats-1.1.31.jar lib/guava-33.4.0-jre.jar lib/failureac
 cess-1.0.2.jar lib/listenablefuture-9999.0-empty-to-avoid-conflict-with
 -guava.jar lib/jsr305-3.0.2.jar lib/checker-qual-3.43.0.jar lib/error_p
 rone_annotations-2.36.0.jar lib/j2objc-annotations-3.0.0.jar lib/jt-tra
 nslate-1.1.31.jar lib/jt-utilities-1.1.31.jar lib/jt-warp-1.1.31.jar li
 b/jt-zonal-1.1.31.jar lib/jt-binarize-1.1.31.jar lib/jt-format-1.1.31.j
 ar lib/jt-colorconvert-1.1.31.jar lib/jt-errordiffusion-1.1.31.jar lib/
 jt-orderdither-1.1.31.jar lib/jt-colorindexer-1.1.31.jar lib/jt-imagefu
 nction-1.1.31.jar lib/jt-piecewise-1.1.31.jar lib/jt-classifier-1.1.31.
 jar lib/jt-rlookup-1.1.31.jar lib/jt-vectorbin-1.1.31.jar lib/jt-shaded
 relief-1.1.31.jar lib/gt-cql-33.2.jar lib/disruptor-1.2.15.jar lib/slf4
 j-api-1.7.13.jar lib/miglayout-3.7-swing.jar lib/gt-api-33.2.jar lib/co
 mmons-pool-1.5.4.jar lib/systems-common-2.1.jar lib/si-quantity-2.1.jar
  lib/si-units-2.1.jar lib/jakarta.annotation-api-1.3.4.jar lib/indriya-
 2.2.jar lib/uom-lib-common-2.2.jar lib/jakarta.inject-api-2.0.1.jar lib
 /apiguardian-api-1.1.2.jar lib/unit-api-2.2.jar lib/jts-core-1.20.0.jar
Main-Class: org.example.SeaChartLauncher

Additional Info and Advice

You don't need the JavaFX SDK

You are sourcing your JavaFX runtime from Maven, so you don't need the JavaFX SDK. I'd advise just deleting it to prevent any confusion.

Don't name a class Main

There is a Main class in the JDK itself, so my IDE was always trying to import that rather than use your Main class.

Name the class that starts your JavaFX app YourAppNameLauncher rather than Starter

This is a minor thing, but by default people seem to name these annoying additional classes that are used to subvert the JavaFX module system as Launcher rather than Starter.

Running JavaFX from the classpath is unsupported

Rather than using a Launcher to load JavaFX from the classpath, delete it completely and update your build and distribution system to ensure JavaFX is loaded from a module (for example by using a Java runtime that includes JavaFX like those provided by Azul or Liberica). Full info on how to get your app to execute in a supported configuration is out of scope for this answer.

Don't use library snapshots

Library snapshots like <version.geotools>34-SNAPSHOT</version.geotools> are inherently unstable and can break your build at any time.

Don't have a dependency on the JavaFX pom

You don't need a direct dependency on the javafx pom artifact.

You can (and should) remove:

<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx</artifactId>
    <version>${version.javafx}</version>
    <type>pom</type>
</dependency>

I think the JavaFX pom is some kind of trickery in the JavaFX Maven usage. It helps select the correct classified libraries that are built with platform specific code for a given architecture (e.g. win x86_64 or Mac aarch64). That all happens automatically when you add a dependency on javafx artifact like javafx-controls without an additional dependency on the javafx pom.

Consider using jpackage

If you need an installer and don't to rely on the user ensuring a correct JDK is installed, you could package your app using package. It is quite tricky and a bit of work though.

Consider creating an assembly

If you don't use jpackage to package your application, you could instead use the maven-assembly-plugin to create a distributable (e.g. a zip file that your users would download, unzip to use). The zip file would include your jar file, and the library files in the relative /lib directory, as well as any other files you may want to provide (e.g. an editable config file). It could also include a shell script to run the app and a JRE that is local to your app so you don't rely on an environmental JRE. Information on creating an assembly is out of scope for this answer.

Shutdown your application cleanly

When the application runs, it does not shut down when you close the JavaFX window. Probably there is something additional you will need to do to get a non-daemon geotools thread to shutdown (probably from the JavaFX application stop() method).

Related

Shading an uber-jar to create a single executable jar for your JavaFX application. This answer includes information on how to make your application work on different architectures than your build machine uses (e.g. creating cross-platform builds and distributable).

Alternate packaging information.

The jar file specification describes how the classpath attribute works in a jar manifest.

Unsupported JavaFX configurations.

Sign up to request clarification or add additional context in comments.

1 Comment

I really appreciate this detailed answer. I will try your changes the next days.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.