2

EDIT: This question should absolutely not be closed. I'm NOT asking how to create an executable jar. A jar doesn't need to be executable to be run from the terminal. For example, if I have have this code: package com.dogzilla.maven.quickstart;

public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World" );
    }
}

...and Maven builds it, it creates quickstart-0.0.1-SNAPSHOT.jar. Which is not, ahem, 'executable'.

I can run it from the terminal quite successfully with this:

java -cp /opt/workspace/eclipse/java/quickstart/target/quickstart-0.0.1-SNAPSHOT.jar com.dogzilla.maven.quickstart.App

The problem, as I have written below, is experienced when using an external dependency. </end edit>

I have a simple Maven project in Eclipse (2020-6). It was set up by doing the following in Eclipse:

1.  File -> New -> Other... Maven -> Maven Project
2.  Used the maven-archetype-quickstart archetype

    Group ID:  com.dogzilla.maven
    Artifact ID:  quickstart

Right click the pom file -> select Add Dependency -> enter:

Group ID: com.google.code.gson
Artifact ID: gson
Version: 2.8.6

Which I verified on https://search.maven.org/

Here's the 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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.dogzilla.maven</groupId>
  <artifactId>quickstart</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <name>quickstart</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.8.6</version>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
        <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.7.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

Here's the source code:

package com.dogzilla.maven.quickstart;

import com.google.gson.Gson;

public class App 
{
    public static void main( String[] args )
    {
        Gson gson = new Gson();
        System.out.println(gson.toJson("Hello World!") );
    }
}

I then right-clicked on the POM file -> Run As -> Maven Build...

and here is the output:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/eclipse/java/plugins/org.eclipse.m2e.maven.runtime.slf4j.simple_1.16.0.20200610-1735/jars/slf4j-simple-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [file:/opt/eclipse/java/configuration/org.eclipse.osgi/5/0/.cp/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/eclipse/java/plugins/org.eclipse.m2e.maven.runtime.slf4j.simple_1.16.0.20200610-1735/jars/slf4j-simple-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [file:/opt/eclipse/java/configuration/org.eclipse.osgi/5/0/.cp/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------< com.dogzilla.maven:quickstart >--------------------
[INFO] Building quickstart 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ quickstart ---
[INFO] Deleting /opt/workspace/eclipse/java/quickstart/target
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ quickstart ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /opt/workspace/eclipse/java/quickstart/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ quickstart ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /opt/workspace/eclipse/java/quickstart/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ quickstart ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /opt/workspace/eclipse/java/quickstart/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ quickstart ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /opt/workspace/eclipse/java/quickstart/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ quickstart ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.dogzilla.maven.quickstart.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.022 s - in com.dogzilla.maven.quickstart.AppTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] 
[INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ quickstart ---
[INFO] Building jar: /opt/workspace/eclipse/java/quickstart/target/quickstart-0.0.1-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.967 s
[INFO] Finished at: 2020-12-13T20:00:27-07:00
[INFO] ------------------------------------------------------------------------

But if I run:

java -cp /opt/workspace/eclipse/java/quickstart/target/quickstart-0.0.1-SNAPSHOT.jar com.dogzilla.maven.quickstart.App

It fails with:

Exception in thread "main" java.lang.NoClassDefFoundError: com/google/gson/Gson
        at com.dogzilla.maven.quickstart.App.main(App.java:13)
Caused by: java.lang.ClassNotFoundException: com.google.gson.Gson
        at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
        ... 1 more

The thing is if I change System.out.println(gson.toJson("Hello World!") ); to plain 'ol System.out.println("Hello World"); it works. So I know the porblem isn't with my java command or how I set the project up outside of the dependency getting resolved.

So the question is, I'm not sure how this is failing on the dependency. I was under the impression Maven managed all that for you. Why is this failing to run?

MGoBlue93
  • 644
  • 2
  • 14
  • 31

4 Answers4

1

The jar file that created from your project only contains classes that written by you. If you need to run the application, you need to include all the necessary dependencies (either direct or transitive) in your classpath.

If you need a single jar file that you can execute without other dependencies, Please follow this to create a fatjar.

MK Tan
  • 586
  • 1
  • 4
  • 12
  • 1
    Thanks, if I add the gson.jar to -cp, like so: java -cp /opt/workspace/eclipse/java/qs/target/qs-0.0.1-SNAPSHOT.jar com.dogzilla.maven.qs.App;/home/dogzilla/.m2/repository/com/google/code/gson/gson/2.3.1/gson-2.3.1.jar It still throws: Exception in thread "main" java.lang.NoClassDefFoundError: com/google/gson/Gson at com.dogzilla.maven.qs.App.main – MGoBlue93 Dec 31 '20 at 02:21
  • you need to use colon (`:`) to separate the classpath like `java -cp /opt/workspace/eclipse/java/qs/target/qs-0.0.1-SNAPSHOT.jar:/home/dogzilla/.m2/repository/com/google/code/gson/gson/2.3.1/gson-2.3.1.jar com.dogzilla.maven.qs.App` – MK Tan Dec 31 '20 at 06:36
0

Use the Maven plugin mentioned in comment https://stackoverflow.com/a/65486325/679858 or use the Maven Shade plugin described here: https://maven.apache.org/plugins/maven-shade-plugin/

Your question was "Why does it run from Eclipse and not from console?": If you run it from Eclipse then the Maven plugin in Eclipse knows the full runtime classpath and so it works. If you run it in console with the created JAR file then that JAR file only contains the classes of your sources but not the transitive dependencies, e.g. Google GSON. But if you use the Maven Assembly plugin or the Maven Shade plugin then you can create a JAR file which contains all the transitive dependencies of your project. That jar could be executed like you wrote it in your example.

Udo
  • 593
  • 5
  • 6
  • I put this in my pom for the Shade plugin gson:gson /home/dogzilla/.m2/repository/com/google/code/gson/gson/2.3.1/gson-2.3.1-sources.jar ...and tried to run with the same results... java -cp /opt/workspace/eclipse/java/qs/target/qs-0.0.1-SNAPSHOT.jar com.dogzilla.maven.qs.App;/home/dogzilla/.m2/repository/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar and get Exception in thread "main" java.lang.NoClassDefFoundError: com/google/gson/Gson – MGoBlue93 Dec 31 '20 at 02:30
  • Look at this doc: https://maven.apache.org/plugins/maven-shade-plugin/usage.html After adding this to your build plugins section of your pom.xml you have to call the goal "package". – Udo Jan 04 '21 at 15:50
-2

You are not providing the path to the additional, transitive, dependencies of your project in the java command, which is why it fails.

You are using maven to build your project, not to execute your class: this is not Maven purpose, although it can do it using maven-exec-plugin. Eclipse is different: it uses Maven information (with m2e) and provides a context to run classes, context that includes dependencies.

The option -cp, explained in depth at Oracle Web Site: java (15), accepts one or more path to list of directories, jar and zip to search for class files:

--class-path classpath, -classpath classpath, or -cp classpath

A semicolon (;) separated list of directories, JAR archives, and ZIP archives to search for class files.

Specifying classpath overrides any setting of the CLASSPATH environment variable. If the class path option isn't used and

classpath isn't set, then the user class path consists of the current directory (.).

As a special convenience, a class path element that contains a base name of an asterisk (*) is considered equivalent to specifying a

list of all the files in the directory with the extension .jar or .JAR . A Java program can't tell the difference between the two invocations. For example, if the directory mydir contains a.jar and b.JAR, then the class path element mydir/* is expanded to A.jar:b.JAR, except that the order of JAR files is unspecified. All .jar files in the specified directory, even hidden ones, are included in the list. A class path entry consisting of an asterisk (*) expands to a list of all the jar files in the current directory. The CLASSPATH environment variable, where defined, is similarly expanded. Any class path wildcard expansion that occurs before the Java VM is started. Java programs never see wildcards that aren't expanded except by querying the environment, such as by calling System.getenv("CLASSPATH").

So you need to fix your command to provides gson and any other compile/runtime/provided dependencies you may add later:

CLASSPATH=''
CLASSPATH+=";$HOME/.m2/repository/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar"
CLASSPATH+=";/opt/workspace/eclipse/java/quickstart/target/quickstart-0.0.1-SNAPSHOT.jar"
CLASSPATH="${CLASSPATH:1}" # remove leading ';'
java -cp "${CLASSPATH}" com.dogzilla.maven.quickstart.App

The man says that you can use ; to separate entries; that is true at least for Windows. However on other Linux based OS (which seems to are on due to /opt being used), this can also be :: that what appassembler does when writing CLASSPATH.

Do note that you will have to adapt the path manually to retarget the actual location of gson.jar (I guessed from what maven does by default).

You can also use appassembler or launch4j to do the trick for you. You can also use build-classpath to build the classpath (it may also work from command line).

E_net4
  • 27,810
  • 13
  • 101
  • 139
NoDataFound
  • 11,381
  • 33
  • 59
  • java -cp /opt/workspace/eclipse/java/qs/target/qs-0.0.1-SNAPSHOT.jar com.dogzilla.maven.qs.App;/home/dogzilla/.m2/repository/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar Exception in thread "main" java.lang.NoClassDefFoundError: com/google/gson/Gson – MGoBlue93 Dec 31 '20 at 02:32
-2

try remove gson from .m2 folder then run maven install again.

Loc Le
  • 537
  • 1
  • 8
  • 21
  • Isn't the point of Maven to manage dependencies for me? Also, next time I run the build, maven will download it anyway. So curious how this suggestion actually fixes the problem. – MGoBlue93 Dec 31 '20 at 02:35