Chiama "java -jar MyFile.jar" con un'opzione classpath aggiuntiva


91

Ho creato un file jar contenente tutte le mie cose compilate. Inoltre il mio script di build ant copia le librerie richieste in una sottocartella "libs". La struttura si presenta così:

MyProgram.jar
libs/

Quindi quando provo a eseguire il mio programma ora ottengo il seguente errore:

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)

Perché succede questo?

Risposte:


151

Usi uno -jar o l'altro -cp , non puoi combinare i due. Se si desidera inserire altri JAR nel percorso di classe, è necessario inserirli nel manifest del JAR principale e quindi utilizzare java -jaro inserire il percorso di classe completo (incluso il JAR principale e le sue dipendenze) -cpe nominare la classe principale in modo esplicito sulla riga di comando

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

(Sto usando la dir/*sintassi che dice al javacomando di aggiungere tutti i .jarfile da una particolare directory al classpath. Nota che *deve essere protetto dall'espansione dalla shell, motivo per cui ho usato virgolette singole.)

Dici che stai usando Ant, quindi per l'approccio manifest alternativo, puoi usare il <manifestclasspath>task di ant dopo aver copiato le dipendenze ma prima di costruire il 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>

Con questa impostazione, java -jar MyProgram.jarfunzionerà correttamente e includerà anche tutti i libsfile JAR nel classpath.


Aggiungendo a sopra, Oppure pensa di aggiungere le voci jar richieste nel file MANIFEST.MF.
Himanshu Bhardwaj

@HimanshuBhardwaj in effetti, ho aggiunto un esempio di come farlo usando<manifestclasspath>
Ian Roberts

Che cosa succede con ":" in "MyProgram.jar: libs / *" è un separatore?
Gobliins

I due punti @Gobliins sono il separatore tra gli elementi in un percorso nei sistemi operativi Linux e Mac. Su Windows dovresti usare punto e virgola ( ;) invece di due punti su Windows è usato per le lettere di unità. L'OP ha utilizzato i due punti e le barre in avanti nella domanda, suggerendo che fossero su Linux o Mac.
Ian Roberts

5
Se le opzioni si escludono a vicenda, la riga di comando dovrebbe stampare un avviso quando vengono utilizzate entrambe. Chi ha tempo per questa curiosità ?!
JJS

22

Quando l' -jaropzione viene utilizzata, l' -cpopzione viene ignorata. L'unico modo per impostare il classpath è utilizzare il file manifest nel jar.

È più facile usare semplicemente l' -cpopzione, aggiungere il tuo file jar a quello, quindi chiamare esplicitamente la classe principale.

Inoltre, supponendo che la /home/user/java/MyProgram/jar/libscartella contenga file jar (al contrario dei file di classe), ciò non funzionerà. Non è possibile specificare una cartella di file jar ma è necessario specificare ogni file jar individualmente nel classpath (vale la pena scrivere un semplice script di shell per farlo per te se c'è un numero significativo di jar).


0

È un po 'complicato. Lo script seguente è un tentativo di ottenere il classpath dal manifest del jar e quindi consentire di aggiungere ulteriori voci di classpath. Ho ottenuto risultati contrastanti con questo, ma voglio condividere lo script comunque in modo che possa essere reso completamente funzionale qui.

Lo script ha due nomi

  • showmanifest
  • calljar

collegando i due file insieme a

ln calljar showmanifest

con calljar -h puoi vedere l'utilizzo.

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

-1

Per test rapidi e una tantum di un'app, puoi semplicemente collegare simbolicamente i file JAR di dipendenza necessari nella directory contenente il file JAR dell'app principale.

Esempio (per un'app app.jarche utilizza la libreria Eclipse SWT, che nel mio caso è stata installata in /usr/share/java):

$ ln -s /usr/share/java/swt.jar .
$ java -jar app.jar
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.