114

I created a jar file containing all my compiled stuff. Additionally my ant build script copies the required libs into a subfolder "libs". The structure looks like this:

MyProgram.jar
libs/

So when I try to run my program now I get the following error:

java -cp ".:/home/user/java/MyProgram/jar/libs" -jar MyProgram.jar
java.lang.ClassNotFoundException: org.postgresql.Driver
    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)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:186)
    at database.PostgresQL.getConnection(PostgresQL.java:38)
    at recommender.dao.Creative2IdxDAO.createCreatives2Idx(Creative2IdxDAO.java:19)
    at main.Main.calculateCorrelationMatrix(Main.java:51)
    at main.Main.main(Main.java:28)
java.lang.NullPointerException
    at recommender.dao.Creative2IdxDAO.createCreatives2Idx(Creative2IdxDAO.java:25)
    at main.Main.calculateCorrelationMatrix(Main.java:51)
    at main.Main.main(Main.java:28)

Why does this happen?

toom
  • 12,864
  • 27
  • 89
  • 128

4 Answers4

176

You use either -jar or -cp, you can't combine the two. If you want to put additional JARs on the classpath then you should either put them in the main JAR's manifest and then use java -jar or you put the full classpath (including the main JAR and its dependencies) in -cp and name the main class explicitly on the command line

java -cp 'MyProgram.jar:libs/*' main.Main

(I'm using the dir/* syntax that tells the java command to add all .jar files from a particular directory to the classpath. Note that the * must be protected from expansion by the shell, which is why I've used single quotes.)

You mention that you're using Ant so for the alternative manifest approach, you can use ant's <manifestclasspath> task after copying the dependencies but before building the JAR.

<manifestclasspath property="myprogram.manifest.classpath" jarfile="MyProgram.jar">
  <classpath>
    <fileset dir="libs" includes="*.jar" />
  </classpath>
</manifestclasspath>

<jar destfile="MyProgram.jar" basedir="classes">
  <manifest>
    <attribute name="Main-Class" value="main.Main" />
    <attribute name="Class-Path" value="${myprogram.manifest.classpath}" />
  </manifest>
</jar>

With this in place, java -jar MyProgram.jar will work correctly, and will include all the libs JAR files on the classpath as well.

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
  • Adding to above, Or think of adding the required jar entries in MANIFEST.MF file. – Himanshu Bhardwaj Apr 10 '13 at 16:13
  • @HimanshuBhardwaj indeed, I've added an example of how to do that using `` – Ian Roberts Apr 10 '13 at 16:16
  • Whats up withe the ':' in the 'MyProgram.jar:libs/*' is this a seperator? – Gobliins Oct 27 '15 at 08:00
  • @Gobliins colon is the separator between items in a path under Linux and Mac operating systems. On Windows you would use semicolon (`;`) instead as colon on Windows is used for drive letters. The OP used colons and forward slashes in their question, suggesting they're on Linux or Mac. – Ian Roberts Oct 27 '15 at 09:31
  • @IanRoberts ok. One more question tough: Can linux understand ./lib or has it to be lib or lib/ in the jars classpath? – Gobliins Oct 27 '15 at 09:51
  • 1
    @Gobliins `./lib` is fine, but it doesn't mean the same as `lib/*`. The `/*` is the bit that says "all the jar files in the directory" as opposed to the directory itself. – Ian Roberts Oct 27 '15 at 20:39
  • 9
    If the options are mutually exclusive, the command line should print a warning when both are used. Who has time for this trivia?! – JJS Jun 01 '16 at 15:33
  • See https://stackoverflow.com/a/5893391/547270 and https://stackoverflow.com/a/10437177/547270 for how to do it in Maven. – scrutari Aug 21 '18 at 15:18
  • `-Xbootclasspath/a:. ` is a option, but may not suitable for all – qxo Nov 06 '19 at 03:10
29

When the -jar option is used the -cp option is ignored. The only way to set the classpath is using manifest file in the jar.

It is easier to just use the -cp option, add your jar file to that, then explicitly call the main class.

Also, assuming the /home/user/java/MyProgram/jar/libs folder contains jar files (as opposed to class files) this won't work. You cannot specify a folder of jar file but must specify each jar file individually in the classpath (it is worth writing a simple shell script to do this for you if there are a significant number of jars).

Jonathan
  • 3,203
  • 2
  • 23
  • 25
  • This is correct. If you do need an ability to stick additional entries to the Class Path, build a Spring Boot executable JAR which supports that ability using the LOADER_PATH variable: https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html#executable-jar.launching. This is Spring Boot-specific, I believe. – Dima Korobskiy Jan 19 '22 at 20:12
0

It is a bit tricky. The following script is an attempt to get the classpath from the manifest of the jar and then allow to add extra classpath entries. I had mixed results with this but want to share the script nevertheless so it might be made fully functional here.

The script has two names

  • showmanifest
  • calljar

by hardlinking the two file together with

ln calljar showmanifest

with calljar -h you can see the usage.

#!/bin/bash
#set -x
# show the manifest of a jar file
# 2012-07-18
# author WF

#
# show usage
#
usage() {
 echo "usage: showmanifest (jarfile | directory jarfile) " 1>&2
 echo "usage: calljar directory jarfile classpath pattern arguments" 1>&2
 echo "             -h|--help " 1>&2
 echo "               show this help and exit" 1>&2
 echo "             -m|--mainclass javaclass" 1>&2
 echo "               mainclass to use (otherwise manifest is inspected)" 1>&2
 exit 1
}

#
# show the manifest of the given jar file
#
show() {
  dir="$1"
  jar="$2"
    fulljar=`find "$dir" -name "$jar"`
    cd /tmp
    mkdir show$$
    cd show$$
    jar xvf $fulljar META-INF/MANIFEST.MF
    cat META-INF/MANIFEST.MF
    cd /tmp
    rm -rf show$$
}

#
# show the classpath of the manifest
#
calljar() {
  dir="$1"
    jar="$2"
    classpath="$3"
    pattern="$4"
    arguments="$5"
    cmd=`show "$dir" "$jar"   | awk -v extracp="$classpath" -v dir="$dir" -v pattern="$pattern" -v jar="$jar" -v mainclass="$mainclass" -v args="$arguments" '
/Main-Class: / { if (mainclass=="") mainclass=$2 }
/^Class-Path:/ { 
  incp=1; 
    cp=$0; 
    gsub("Class-Path: ","",cp) 
    next
}
/^ .*$/ && incp { 
    line=substr($0,2)
  # remove carriage return (if any)
  cp=cp line
}
END { 
  # we do not like carriage returns
  gsub("\\r","",cp)
  gsub("\\r","",mainclass)
    # we do not like blanks ...
  gsub(" ","",cp)
    gsub(pattern,":"dir"/"pattern,cp)
  print "java -cp " extracp cp ":"dir"/"jar " " mainclass " " args
}
    '`
  #echo $cmd
    $cmd
}


# echo $# arguments found: $*
# parse command line options
while true; do
# echo "option $1"
  case "$1" in
    # options without arguments
    -h|--help) usage;;
         # for options with required arguments, an additional shift is required
        -m|--mainclass) mainclass=$2; shift;;
      (--) shift; break;;
      (-*) echo "$0: error - unrecognized option $1" 1>&2; usage;;
    (*) dir=$1;shift;break;;
  esac
  shift
done

#echo "argcount=$#"
case  $# in
  0) dir=`dirname "$dir"`
       jar=`basename "$dir"`
         show "$dir" "$jar";;
  1) jar="$1"
         show "$dir" "$jar";;
  2) usage;;
    3) usage;;
  *) jar="$1"; shift;
         classpath="$1"; shift;
         pattern="$1"; shift;
         arguments="$@";
    #echo "mainclass=${mainclass}"
    #echo "classpath=${classpath}"

  #echo calljar "${dir}" "${jar}" "${classpath}" "$pattern" "$arguments"
    calljar "$dir" "$jar" "$classpath" "$pattern" "$arguments"
    ;;
esac
Wolfgang Fahl
  • 15,016
  • 11
  • 93
  • 186
-1

For quick, one-off tests of an app, you can simply symlink the needed dependency JAR files into the directory containing the main app JAR file.

Example (for an app app.jar which uses the Eclipse SWT library, which in my case was installed in /usr/share/java):

$ ln -s /usr/share/java/swt.jar .
$ java -jar app.jar
ack
  • 7,356
  • 2
  • 25
  • 20