Can't run jar file of JavaFx application on Linux [duplicate] - java

I'm trying to create a JFX11 self-containing jar using maven dependencies. From the research I've done, it seems the best way to do this is through the maven shade plugin. However, When I run it, I get the this error:
Error: JavaFX runtime components are missing, and are required to run this application
I don't understand why this is happening. What am I messing up? Is there a better way to do this? I've also tried the maven assembly plugin with the same message.
pom file for reference
<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>Application</groupId>
<artifactId>Main</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>SpaceRunner</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>11</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>10</release>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>Application.Main</mainClass>
</configuration>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>
Application.Main
</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>Application.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

UPDATE 10/2021
Since JavaFX 16 a warning is displayed when JavaFX doesn't run on the module path, which is the case of an uber/fat jar:
$ java -jar myFatJar-1.0-SNAPSHOT.jar
Oct 02, 2021 1:45:21 PM com.sun.javafx.application.PlatformImpl startup
WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module #14c24f4c'
Also, you get a warning from the shade plugin itself:
[WARNING] Discovered module-info.class. Shading will break its strong encapsulation.
While these warnings can be initially ignored, there is a reason for them.
As explained in this CSR:
JavaFX is built and distributed as a set of named modules, each in its own modular jar file, and the JavaFX runtime expects its classes to be loaded from a set of named javafx.* modules, and does not support loading those modules from the classpath.
And:
when the JavaFX classes are loaded from the classpath, it breaks encapsulation, since we no longer get the benefit of the java module system.
Therefore, even this widely accepted answer explains how can an uber/fat jar can be created on Maven projects, its use is discouraged, and other modern alternatives to distribute your application, like jlink, jpackage or native-image, should be used.
ORIGINAL ANSWER
This answer explains why a fat/uber jar fails on JavaFX 11. In short:
This error comes from sun.launcher.LauncherHelper in the java.base module. The reason for this is that the Main app extends Application and has a main method. If that is the case, the LauncherHelper will check for the javafx.graphics module to be present as a named module. If that module is not present, the launch is aborted.
And already proposes a fix for Gradle.
For Maven the solution is exactly the same: provide a new main class that doesn't extend from Application.
You will have new class in your application package (bad name):
// NewMain.java
public class NewMain {
public static void main(String[] args) {
Main.main(args);
}
}
And your existing Main class, as is:
//Main.java
public class Main extends Application {
#Override
public void start(Stage stage) {
...
}
public static void main(String[] args) {
launch(args);
}
}
Now you need to modify your pom and set your main class for the different plugins:
<mainClass>application.NewMain</mainClass>
Platform-specific Fat jar
Finally, with the shade plugin you are going to produce a fat jar, on your machine.
This means that, so far, your JavaFX dependencies are using a unique classifier. If for instance you are on Windows, Maven will be using internally the win classifier. This has the effect of including only the native libraries for Windows.
So you are using:
org.openjfx:javafx-controls:11
org.openjfx:javafx-controls:11:win
org.openjfx:javafx-graphics:11
org.openjfx:javafx-graphics:11:win <-- this contains the native dlls for Windows
org.openjfx:javafx-base:11
org.openjfx:javafx-base:11:win
Now, if you produce the fat jar, you will bundle all those dependencies (and those other regular third party dependencies from your project), and you will be able to run your project as:
java -jar myFatJar-1.0-SNAPSHOT.jar
While this is very nice, if you want to distribute you jar, be aware that this jar is not cross-platform, and it will work only on your platform, in this case Windows.
Cross-Platform Fat Jar
There is a solution to generate a cross-platform jar that you can distribute: include the rest of the native libraries of the other platforms.
This can be easily done, as you just need to include the graphics module dependencies for the three platforms:
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>11</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics </artifactId>
<version>11</version>
<classifier>win</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics </artifactId>
<version>11</version>
<classifier>linux</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics </artifactId>
<version>11</version>
<classifier>mac</classifier>
</dependency>
</dependencies>
Size
There is a main issue with this approach: the size. As you can see in this other answer, if you use the WebView control, you will be bundling around 220 MB due to the WebKit native libraries.

Related

EXE using launch4j does not work (javafx project), JAR files need to be ran from the console specifying the VM argument of the javafx location

created a very simple gui with javafx trying to test out how creating a javafx exe would work, extracted a runnable jar file from eclipse but that doesnt run unless i use the cmd and add the VM argument --module-path "" --add-modules=javafx.controls,javafx.fxml, so i tried to make an exe from that jar file using launch4j, it would redirect me to java.com/downloads so i embedded the JRE bin and lib folders with the exe file now it does not run at all.
Am i missing something or doing something wrong?
Wrapping into native container:
For Wrapping your application into an EXE or DEB, or..., you should not use launch4j, but "jpackage". It is the official tool from oracle for wrapping JARs into EXEs or DEBs, .. and it is included in JDK 17 LTS. It works like a charm. Please note: Your projects don't have to use JDK 17, they can use JDK 11 as well.
General about JFX:
I can't tell you why, but JFX does NOT like it, if the class, which contains the main() extends from another class. It took me hours to find that out. It may sound stupid, uneccessary or similiar, but I can only advise you to do this workaround:
Mainclass:
package com.wedasoft.FxMultiMessageSender;
public class FxMultiMessageSenderMain {
public static void main(String[] args) {
App.main(args);
}
}
JFX-Application-Starting-Class:
package com.wedasoft.FxMultiMessageSender;
public class App extends Application {
public static final String APPLICATION_NAME = "xxxxxxxx";
private Stage primaryStage;
private Scene primaryScene;
public static void main(String[] args) {
//createDatabase();
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
System.out.println("Starting " + APPLICATION_NAME);
}
}
Not really working JARs:
Your question sounds like you are using a normal Java project and embedded the JFX libraries. If so, I can only advise you to use Maven or Gradle for at least JFX projects; you won't get happy without one of them.
If you use Maven, then try to integrated your pom.xml with the following pom.xml. You do not need to specify any VM arguments. You need to adjust the main class <mainClass>com.wedasoft.FxMultiMessageSender.FxMultiMessageSenderMain</mainClass> in the javafx-maven-plugin and in the maven-shade-plugin.
<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.wedasoft</groupId>
<artifactId>FxMultiMessageSender</artifactId>
<version>0.0.2</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<javafx.version>17</javafx.version>
</properties>
<dependencies>
<!-- javafx -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>${javafx.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<executions>
<execution>
<!-- Default configuration for running -->
<!-- Usage: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<mainClass>com.wedasoft.FxMultiMessageSender.FxMultiMessageSenderMain</mainClass>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.wedasoft.FxMultiMessageSender.FxMultiMessageSenderMain</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

neo4j procedure depending on signed jar (mapdb)

I have created a neo4j user-defined procedure. It also compiles and works in neo4j.
Recently though, I added a dependency to my procedure plugin that prevents neo4j from starting when I try to run the newly built jar. Concretely, I receive following exception at the bottom of the stack trace:
Caused by: java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
The only thing I changed is to add MapDB to my dependencies. So I suspect that it depends on some signed artifact. As it turns out neo4j plugins are "uber-jars" (i.e., shaded jars) which doesn't work very well with signed dependencies.
I figured I could try to exclude MapDB from the shading by changing the scope to provided and additionally adding the mapdb jar to plugins folder of neo4j. So the plugins folder of neo4j now includes both mapdb.jar and myprocedure.jar.
This doesn't seem to work though: neo4j starts, but when calling my procedure I receive a ClassNotFound Exception.
Any ideas on how I can solve this dilemma? I really depend on something like MapDB as my graph is very large and keeping everything in my procedure in-memory regularly leads to memory exceptions.
Many thanks in advance!
The important part of my pom.xml should it help (I started off with the procedure template so it still looks quite similar):
<dependencies>
<!-- other dependencies that don't make a difference ... -->
<dependency>
<groupId>org.mapdb</groupId>
<artifactId>mapdb</artifactId>
<version>3.0.7</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j</artifactId>
<version>${neo4j.version}</version>
<scope>provided</scope>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>org.neo4j.test</groupId>
<artifactId>neo4j-harness</artifactId>
<version>${neo4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver</artifactId>
<version>1.4.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<!-- Neo4j Procedures require Java 8 -->
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<!-- This generates a jar-file with our procedure code,
plus any dependencies marked as `compile` scope.
This should then be deployed in the `plugins` directory
of each Neo4j instance in your deployment.
After a restart, the procedure is available for calling. -->
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
So, one solution that worked was to use the maven-assembly-plugin instead of the maven-shade-plugin. The assembly plugin creates a standalone jar with all dependencies without re-packaging all dependencies.
Of course, I had to remove the <scope>provided</scope> for mapdb so it is also included in the assembled jar.
The rest is just replacing the shade plugin with following snippet (thanks to this question here):
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
Afterwards I received another exception which I thought was due to a wrong signature:
Caused by: java.lang.VerifyError ...
Turns out this was just because I had another procedure in neo4j that uses the same dependencies as mapdb. Quick fix was to remove that procedure library.
Of course, there are also other solutions, including removing the signature, or re-signing it. However, I explicitly did not want to remove any signatures.
I'm not aware that neo4j is checking any jar signatures by default. I suspect the repackaging of mapdb artifact being the culprit.
To validate that, remove the shade or assembly plugin in and build a jar that solely contains your code. Copy that jar and the mapdb.jar into the plugin folder and observe logs during a restart.

How do you replace the class of a Maven dependency?

There is a class in a maven dependency that is incompatible with Java 8.
How do you properly fix that problem?
Right now I'm doing the following:
Create a package with the same name
Create a class with the same name in that package
Copy and paste the code
Fix the incompatible API call
The problem is that this class contains API calls to restricted classes and although I changed the Eclipse compiler settings (Window -> Preferences -> Java -> Compiler -> Error/Warnings -> Deprecated and restricted API -> Forbidden reference (access rule): Error -> Warning) to allow access the project will only compile sometimes. If it doesn't compile I'll get a "can't find symbol" error.
Edit:
Here are the details you asked for:
Dependency: http://mvnrepository.com/artifact/com.sun.xml.wss/xws-security/3.0
Class: EncryptionProcessor
Necessary change:
// Change line 1053 FROM:
// _dataEncryptor = XMLCipher.getInstance(dataEncAlgo, _dataCipher);
// TO:
_dataEncryptor = XMLCipher.getInstance(dataEncAlgo);
Edit-2:
Maven build error:
[ERROR] symbol: class XMLCipher
[ERROR] location: class com.sun.xml.wss.impl.apachecrypto.EncryptionProcessor
[ERROR] /C:/Users/{name}/development/eclipse_workspace/git/xws-security/src/main/java/com/sun/xml/wss/impl/apachecrypto/EncryptionProcessor.java:[1482,98] cannot find symbol
Here is a detailed guide describing what I did exactly:
Create new Maven project in Eclipse
Configure Maven settings of new project (Important: Use the same group and artifact ID and only change the version number)
<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>com.sun.xml.wss</groupId>
<artifactId>xws-security</artifactId>
<version>3.0-java8-fix</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
</project>
Add dependency of bugged JAR
<dependencies>
<dependency>
<groupId>com.sun.xml.wss</groupId>
<artifactId>xws-security</artifactId>
<version>3.0</version>
<exclusions>
<exclusion>
<artifactId>xmldsig</artifactId>
<groupId>javax.xml.crypto</groupId>
</exclusion>
<exclusion>
<artifactId>activation</artifactId>
<groupId>javax.activation</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
Create Java file in the same package of class that needs to be fixed
package com.sun.xml.wss.impl.apachecrypto;
public class EncryptionProcessor {
// The FIX goes here
}
Add Maven shade build plug in to handle creation of patched JAR file (this is not the only plug in to handle this kind of task - e.g. dependency:unpack)
<build>
<plugins>
<!-- plug in for creation of patched JAR file -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>com.sun.xml.wss:xws-security:3.0</artifact>
<includes>
<include>**/*.class</include>
<include>**/*.xml</include>
</includes>
<excludes>
<exlude>
com/sun/xml/wss/impl/apachecrypto/EncryptionProcessor.class
</exlude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Include patched JAR in other projects as necessary (Note: If you experience ClassNotFoundExceptions or similar errors do this: Right-click on the project -> Properties -> Maven -> "Resolve dependencies from Workspace projects":false)
In case you are not familiar with Maven. Here is the complete pom.xml: http://pastebucket.com/88444
Similar to Steven S.'s answer, but using the maven-dependency-plugin. Based on this blog post.
I changed the name of the patched library (not the version), but it depends on your needs what works better for you.
The dependency on the original library should be marked as <optional>true</optional>. Otherwise, the projects that depend on your patched library will also depend on the original library, which means that both the patched and the original version will be on the classpath, which can lead to all kinds of problems.
If your project is a child project, you can still use a completely different groupId and version than your parent pom. Doesn't matter.
You can exclude the classes you patch from unpacking, but it's probably not necessary, because Maven will first unpack the original library and then compile your new version, which means that the original classes are overwritten. Nice!
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<!-- remove this if you don't have a parent pom -->
<parent>
<groupId>my.company</groupId>
<artifactId>my.company</artifactId>
<version>1.2.3</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>com.foo</groupId>
<artifactId>foo-bar-patched</artifactId>
<version>4.5.6</version>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.foo</groupId>
<artifactId>foo-bar</artifactId>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<!-- excludes are probably not necessary -->
<!-- <excludes>**/Foo.class,**/Bar.class</excludes> -->
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.foo</groupId>
<artifactId>foo-bar</artifactId>
<version>4.5.6</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>
General solution:
download all project sources
apply your modification
use version control so that change isn't lost
change version in pom.xml, for example from 3.0 to 3.0-patched
launch maven build
copy generated artifacts to you repository/Artifactory, if you use one
change dependency version in your own project

Error while compiling when using AspectJ compiler instead of Javac

I have a multi module project. The aspect is currently added to the "core" project. When doing a mvn clean install here it works. However trying to do a mvn clean install on parent project it fails with this error when compiling one of the other projects:
The type org.hibernate.annotations.CacheConcurrencyStrategy cannot be resolved. It is indirectly referenced from required .class files
If I add the Hibernate core dependency in that project too it works, but adding dependencies to projects that shouldn't have the dependency does not make sense - so it is not a solution. When compiling with javac it works fine.
What is the reason? And how can I fix it so I can use the AspectJ compiler without leaking dependencies to projects that shouldn't have that?
I have this configuration in the parent POM:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.5</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<complianceLevel>1.6</complianceLevel>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Update
I just found out. Running mvn clean install fails every time. However, running mvn [clean] install one time fails. Then running mvn install without clean works. I see that the builddef.lst in the target folder is the reason why it works and fails based on whether or not you run clean. So now my question is: How do you automatically generate this file?
Parent POM-file:
<?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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>core-lib</artifactId>
<name>core-lib</name>
<packaging>pom</packaging>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.5</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<complianceLevel>1.6</complianceLevel>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.4</version>
</dependency>
</dependencies>
<modules>
<module>core-xyz</module>
<module>core-xyz2</module>
</modules>
</project>
Enable debug on the maven invocation to dig deeper. You should observe that the aspectj compile is only being invoked during the first maven invocation with the clean. Since the builddef.lst already exists after the first invocation, invoking without clean skips the aspectj compile.
This aspectj compile plugin behavior has been observed previously and was described here:
http://out-println.blogspot.com/2007/08/compile-time-checks-with-aspectj-part-2.html?m=1
You will need to look deeper to address the underlying issue, but as one commenter has already suggested, the aspectj compiler should only be enabled in modules that require it.
Otherwise, the additional dependencies are required for the aspectj compile, as you have already observed. I have incorporated aspectj compile into my own work without issue by restricting it to only the modules that require it.
According to the AspectJ compiler Maven plugin you could set up the argumentFileName to locate an existing builddef.lst.
So you can generate the builddef.lst and copy it to your resources folder, and instruct the AspectJ Maven plugin to use that file.

NoClassDefFoundError on Maven dependency

My first use of Maven and I'm stuck with dependencies.
I created a Maven project with Eclipse and added dependencies, and it was working without problems.
But when I try to run it via command line:
$ mvn package # successfully completes
$ java -cp target/bil138_4-0.0.1-SNAPSHOT.jar tr.edu.hacettepe.cs.b21127113.bil138_4.App # NoClassDefFoundError for dependencies
It downloads dependencies, successfully builds, but when I try to run it, I get NoClassDefFoundError:
Exception in thread "main" java.lang.NoClassDefFoundError: org/codehaus/jackson/JsonParseException
at tr.edu.hacettepe.cs.b21127113.bil138_4.db.DatabaseManager.<init>(DatabaseManager.java:16)
at tr.edu.hacettepe.cs.b21127113.bil138_4.db.DatabaseManager.<init>(DatabaseManager.java:22)
at tr.edu.hacettepe.cs.b21127113.bil138_4.App.main(App.java:10)
Caused by: java.lang.ClassNotFoundException: org.codehaus.jackson.JsonParseException
at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
... 3 more
My pom.xml is like this:
<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>tr.edu.hacettepe.cs.b21127113</groupId>
<artifactId>bil138_4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>bil138_4</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Can anyone help me?
By default, Maven doesn't bundle dependencies in the JAR file it builds, and you're not providing them on the classpath when you're trying to execute your JAR file at the command-line. This is why the Java VM can't find the library class files when trying to execute your code.
You could manually specify the libraries on the classpath with the -cp parameter, but that quickly becomes tiresome.
A better solution is to "shade" the library code into your output JAR file. There is a Maven plugin called the maven-shade-plugin to do this. You need to register it in your POM, and it will automatically build an "uber-JAR" containing your classes and the classes for your library code too when you run mvn package.
To simply bundle all required libraries, add the following to your POM:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
Once this is done, you can rerun the commands you used above:
$ mvn package
$ java -cp target/bil138_4-0.0.1-SNAPSHOT.jar tr.edu.hacettepe.cs.b21127113.bil138_4.App
If you want to do further configuration of the shade plugin in terms of what JARs should be included, specifying a Main-Class for an executable JAR file, and so on, see the "Examples" section on the maven-shade-plugin site.
when I try to run it, I get NoClassDefFoundError
Run it how? You're probably trying to run it with eclipse without having correctly imported your maven classpath. See the m2eclipse plugin for integrating maven with eclipse for that.
To verify that your maven config is correct, you could run your app with the exec plugin using:
mvn exec:java -D exec.mainClass=<your main class>
Update: First, regarding your error when running exec:java, your main class is tr.edu.hacettepe.cs.b21127113.bil138_4.App. When talking about class names, they're (almost) always dot-separated. The simple class name is just the last part: App in your case. The fully-qualified name is the full package plus the simple class name, and that's what you give to maven or java when you want to run something. What you were trying to use was a file system path to a source file. That's an entirely different beast. A class name generally translates directly to a class file that's found in the class path, as compared to a source file in the file system. In your specific case, the class file in question would probably be at target/classes/tr/edu/hacettepe/cs/b21127113/bil138_4/App.class because maven compiles to target/classes, and java traditionally creates a directory for each level of packaging.
Your original problem is simply that you haven't put the Jackson jars on your class path. When you run a java program from the command line, you have to set the class path to let it know where it can load classes from. You've added your own jar, but not the other required ones. Your comment makes me think you don't understand how to manually build a class path. In short, the class path can have two things: directories containing class files and jars containing class files. Directories containing jars won't work. For more details on building a class path, see "Setting the class path" and the java and javac tool documentation.
Your class path would need to be at least, and without the line feeds:
target/bil138_4-0.0.1-SNAPSHOT.jar:
/home/utdemir/.m2/repository/org/codehaus/jackson/jackson-core-asl/1.9.6/jackson-core-asl-1.9.6.jar:
/home/utdemir/.m2/repository/org/codehaus/jackson/jackson-mapper-asl/1.9.6/jackson-mapper-asl-1.9.6.jar
Note that the separator on Windows is a semicolon (;).
I apologize for not noticing it sooner. The problem was sitting there in your original post, but I missed it.
You have to make classpath in pom file for your dependency. Therefore you have to copy all the dependencies into one place.
Check my blog.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>$fullqualified path to your main Class</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
This is due to Morphia jar not being part of your output war/jar. Eclipse or local build includes them as part of classpath, but remote builds or auto/scheduled build don't consider them part of classpath.
You can include dependent jars using plugin.
Add below snippet into your pom's plugins section
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
For some reason, the lib is present while compiling, but missing while running.
My situation is, two versions of one lib conflict.
For example, A depends on B and C, while B depends on D:1.0, C depends on D:1.1, maven may
just import D:1.0. If A uses one class which is in D:1.1 but not in D:1.0, a NoClassDefFoundError will be throwed.
If you are in this situation too, you need to resolve the dependency conflict.
I was able to work around it by running mvn install:install-file with -Dpackaging=class. Then adding entry to POM as described here:
Choosing to Project -> Clean should resolve this

Categories