Panggil "java -jar MyFile.jar" dengan opsi jalur kelas tambahan

93

Saya membuat file jar yang berisi semua barang saya yang dikompilasi. Selain itu skrip ant build saya menyalin lib yang diperlukan ke dalam subfolder "libs". Strukturnya terlihat seperti ini:

MyProgram.jar
libs/

Jadi ketika saya mencoba menjalankan program saya sekarang saya mendapatkan kesalahan berikut:

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)

Mengapa ini terjadi?

toom
sumber

Jawaban:

153

Anda menggunakan salah satu -jar atau -cp , Anda tidak dapat menggabungkan keduanya. Jika Anda ingin meletakkan JAR tambahan pada classpath, Anda harus meletakkannya di manifes JAR utama dan kemudian menggunakan java -jaratau Anda meletakkan classpath lengkap (termasuk JAR utama dan dependensinya) di dalam -cpdan memberi nama class utama secara eksplisit pada baris perintah

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

(Saya menggunakan dir/*sintaks yang memberi tahu javaperintah untuk menambahkan semua .jarfile dari direktori tertentu ke classpath. Perhatikan bahwa *harus dilindungi dari ekspansi oleh shell, itulah sebabnya saya menggunakan tanda kutip tunggal.)

Anda menyebutkan bahwa Anda menggunakan Ant jadi untuk pendekatan manifes alternatif, Anda dapat menggunakan <manifestclasspath>tugas ant setelah menyalin dependensi tetapi sebelum membuat 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>

Dengan ini, java -jar MyProgram.jarakan bekerja dengan benar, dan akan menyertakan semua libsfile JAR di classpath juga.

Ian Roberts
sumber
Menambah di atas, Atau pikirkan untuk menambahkan entri jar yang diperlukan dalam file MANIFEST.MF.
Himanshu Bhardwaj
@HimanshuBhardwaj memang, saya telah menambahkan contoh bagaimana melakukan itu menggunakan<manifestclasspath>
Ian Roberts
Ada apa dengan ':' di 'MyProgram.jar: libs / *' apakah ini pemisah?
Gobliins
@Gobliins titik dua adalah pemisah antara item dalam jalur di sistem operasi Linux dan Mac. Di Windows, Anda akan menggunakan titik koma ( ;) karena titik dua di Windows digunakan untuk huruf drive. OP menggunakan titik dua dan garis miring dalam pertanyaan mereka, menunjukkan bahwa mereka menggunakan Linux atau Mac.
Ian Roberts
5
Jika opsinya sama-sama eksklusif, baris perintah harus mencetak peringatan saat keduanya digunakan. Siapa yang punya waktu untuk hal-hal sepele ini ?!
JJS
22

Ketika -jaropsi digunakan, -cpopsi tersebut diabaikan. Satu-satunya cara untuk menyetel classpath adalah menggunakan file manifes di dalam jar.

Lebih mudah menggunakan -cpopsi, menambahkan file jar Anda ke sana, lalu memanggil kelas utama secara eksplisit.

Juga, dengan asumsi /home/user/java/MyProgram/jar/libsfolder berisi file jar (sebagai lawan file kelas) ini tidak akan berfungsi. Anda tidak dapat menentukan folder file jar tetapi harus menentukan setiap file jar satu per satu di classpath (ada baiknya menulis skrip shell sederhana untuk melakukan ini untuk Anda jika ada sejumlah besar jar).

Jonathan
sumber
0

Ini agak rumit. Skrip berikut adalah upaya untuk mendapatkan classpath dari manifes jar dan kemudian mengizinkan untuk menambahkan entri classpath tambahan. Saya memiliki hasil yang beragam dengan ini tetapi ingin membagikan skrip agar tetap berfungsi penuh di sini.

Skrip ini memiliki dua nama

  • showmanifest
  • calljar

dengan menautkan kedua file tersebut bersama-sama dengan

ln calljar showmanifest

dengan calljar -h Anda dapat melihat penggunaannya.

#!/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
sumber
-1

Untuk pengujian cepat dan satu kali suatu aplikasi, Anda cukup menghubungkan file JAR dependensi yang diperlukan ke direktori yang berisi file JAR aplikasi utama.

Contoh (untuk aplikasi app.jaryang menggunakan pustaka Eclipse SWT, yang dalam kasus saya dipasang di /usr/share/java):

$ ln -s /usr/share/java/swt.jar .
$ java -jar app.jar
ack
sumber