[GIS] Geotools with maven: java.lang.noclassdeffounderror while starting app’s jar

geotoolsjavamaven

In my Idea everything works fine but when I try to start my jar which was built by maven there are some errors:

1 – Caused by: java.lang.RuntimeException: Exception in Application start method

2 – Caused by: java.lang.NoClassDefFoundError: org/geotools/data/FeatureSource

3 – Caused by: java.lang.ClassNotFoundException: org.geotools.data.FeatureSource

It looks like it can't find FeatureSource class.

I experimented with java code and found out that it can't find and other GeoTools classes. This error becomes when I try to create object of any GeoTool's class. So I can get the same error for FeatureSource or StyleFactory or MapContent.

My classpath is ok and all dependencies are also ok. Maven put all my dependencies (and GeoTools jars and their subdependecies) to target\lib directory and other not GeoTools libs (like mysql-connector-java) work fine.

Jar stats fine if I remove calling to GeoTools classes in the code. I don't print my java code because issue isn't in java code but in some SDK or maven (pom) settings.

Here is my dependencies:

enter image description here

I start jar by "java -jar siveria.avl-1.0-SNAPSHOT.jar" in cmd. My lib dir is in ${project.build.directory} so it has project_name\target\lib\ path and it's not in project_name\target\classes\ dir in my classes dir there are only my main project's package dir and dirs from resources dir.

Here is my Project's target directory structure:

enter image description here

Here is my Project's src directory structure:

enter image description here

Here is my POM:

<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>AVL</groupId>
<artifactId>siveria.avl</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>siveria.avl</name>
<url>http://maven.apache.org</url>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <geotools.version>2.7-M2</geotools.version>
</properties>

<repositories>
    <repository>
          <id>maven2-repository.dev.java.net</id>
          <name>Java.net repository</name>
          <url>http://download.java.net/maven/2</url>
    </repository>
    <repository>
        <id>osgeo</id>
        <name>Open Source Geospatial Foundation Repository</name>
        <url>http://download.osgeo.org/webdav/geotools/</url>
    </repository>
    <repository>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
        <id>boundless</id>
        <name>Boundless Maven Repository</name>
        <url>http://repo.boundlessgeo.com/main</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.controlsfx</groupId>
        <artifactId>controlsfx</artifactId>
        <version>8.40.14</version>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-shapefile</artifactId>
        <version>19-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-swing</artifactId>
        <version>19-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.jfree</groupId>
        <artifactId>fxgraphics2d</artifactId>
        <version>1.3</version>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-epsg-hsql</artifactId>
        <version>19-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>javafx</artifactId>
        <version>${java.version}</version>
        <scope>system</scope>
        <systemPath>${project.basedir}/lib/jfxrt.jar</systemPath>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.38</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
                <archive>
                    <index>true</index>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                        <mainClass>AVL.Main</mainClass>
                    </manifest>
              </archive>
            </configuration>
        </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <source>1.8</source>
            <target>1.8</target>
        </configuration>
    </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
            <execution>
                <id>copy</id>
                <phase>package</phase>
                <goals>
                    <goal>copy-dependencies</goal>
                </goals>
                <configuration>
                    <outputDirectory>
                        ${project.build.directory}/lib
                    </outputDirectory>
                </configuration>
            </execution>
        </executions>
    </plugin>
</plugins>
  <resources>
      <resource>
          <directory>src/main/java/AVL/resources</directory>
          <includes>
              <include>**/*.fxml</include>
              <include>**/*.css</include>
              <include>**/*.jpg</include>
              <include>**/*.png</include>
              <include>**/*.ico</include>
              <include>**/*.cpg</include>
              <include>**/*.dbf</include>
              <include>**/*.prj</include>
              <include>**/*.qix</include>
              <include>**/*.ico</include>
              <include>**/*.shp</include>
              <include>**/*.shx</include>
              <include>**/*.sld</include>
          </includes>
      </resource>

  </resources>

Here is my stacktrace:

Exception in Application start method 
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Lau
ncherImpl.java:389)
    at com.sun.javafx.application.LauncherImpl.launchApplication 
(LauncherImpl.java:328)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)

Caused by: java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1 
    (LauncherImpl.java:917)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155 
    (LauncherImpl.java:182)
    at java.lang.Thread.run(Unknown Source)

Caused by: java.lang.NoClassDefFoundError: org/geotools/data/FeatureSource
    at AVL.controllers.MapController.initialize(MapController.java:32)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
    at AVL.Main.start(Main.java:29)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162
    (LauncherImpl.java:863)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175 
    (PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173 
    (PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174 
    (PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run 
    (InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148 
    (WinApplication.java:191)
    ... 1 more

Caused by: java.lang.ClassNotFoundException: 
    org.geotools.data.FeatureSource
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    ... 19 more
    Exception running application AVL.Main

Best Answer

To make a java program run you need to include all of its dependencies on the classpath so that the JVM can find them or include them in the jar itself so that it is truly portable.

In the first case, this can be achieved by adding your maven repository to the command line:

java -jar -cp .:~/.m2/repository/ siveria.avl-1.0-SNAPSHOT.jar

or for the second (and probably better) case you need to create an "uber" or "fat" jar. This combines all the needed jars into one big one that you can execute with no classpath. This is accomplished using the maven shade plugin since this handles the services/spi metadata files that GeoTools uses to handle datastore lookups.

You need to include something like:

<build>
  <plugins>
      <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <configuration>
              <encoding>UTF-8</encoding>
              <target>1.8</target>
              <source>1.8</source>
          </configuration>
      </plugin>
      <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-shade-plugin</artifactId>
          <version>1.3.1</version>
          <executions>
              <execution>
                  <phase>package</phase>
                  <goals>
                      <goal>shade</goal>
                  </goals>
                  <configuration>
                      <transformers>
                          <!-- This bit sets the main class for the executable jar as you otherwise -->
                          <!-- would with the assembly plugin                                       -->
                          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                              <manifestEntries>
                                  <Main-Class>org.geotools.demo.Quickstart</Main-Class>
                              </manifestEntries>
                          </transformer>
                          <!-- This bit merges the various GeoTools META-INF/services files         -->
                          <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                      </transformers>
                  </configuration>
              </execution>
          </executions>
      </plugin>
  </plugins>
</build>

A final option is to use Maven to execute the program using its knowledge of your dependencies. By including something like and then using mvn exec:java:

         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.2.1</version>
            <configuration>
               <mainClass>org.geotools.tutorial.quickstart.Quickstart</mainClass>
               <commandlineArgs>../../data/ne_10m_admin_0_countries.shp</commandlineArgs>
             </configuration>
          </plugin>