Appelez «java -jar MyFile.jar» avec une option de chemin de classe supplémentaire

91

J'ai créé un fichier jar contenant tous mes éléments compilés. De plus, mon script de construction de fourmi copie les bibliothèques requises dans un sous-dossier "libs". La structure ressemble à ceci:

MyProgram.jar
libs/

Ainsi, lorsque j'essaye d'exécuter mon programme maintenant, j'obtiens l'erreur suivante:

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)

Pourquoi cela arrive-t-il?

toom
la source

Réponses:

151

Vous utilisez l' un -jar ou l' autre-cp , vous ne pouvez pas combiner les deux. Si vous souhaitez mettre des JAR supplémentaires sur le chemin de classe, vous devez soit les mettre dans le manifeste du JAR principal, puis utiliser, java -jarsoit mettre le -cpchemin de classe complet (y compris le JAR principal et ses dépendances) et nommer la classe principale explicitement sur la ligne de commande

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

(J'utilise la dir/*syntaxe qui indique à la javacommande d'ajouter tous les .jarfichiers d'un répertoire particulier au chemin de classe. Notez que le *doit être protégé de l'expansion par le shell, c'est pourquoi j'ai utilisé des guillemets simples.)

Vous mentionnez que vous utilisez Ant donc pour l'approche alternative du manifeste, vous pouvez utiliser la <manifestclasspath>tâche de fourmi après avoir copié les dépendances mais avant de construire le 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>

Avec cela en place, java -jar MyProgram.jarfonctionnera correctement et inclura également tous les libsfichiers JAR sur le chemin de classe.

Ian Roberts
la source
Ajout à ci-dessus, Ou pensez à ajouter les entrées jar requises dans le fichier MANIFEST.MF.
Himanshu Bhardwaj
@HimanshuBhardwaj en effet, j'ai ajouté un exemple de la façon de le faire en utilisant<manifestclasspath>
Ian Roberts
Que se passe-t-il avec le ':' dans le 'MyProgram.jar: libs / *' est-ce un séparateur?
Gobliins
@Gobliins deux points est le séparateur entre les éléments d'un chemin sous les systèmes d'exploitation Linux et Mac. Sous Windows, vous utiliseriez le point-virgule ( ;) à la place car le signe deux-points sur Windows est utilisé pour les lettres de lecteur. L'OP a utilisé des deux-points et des barres obliques dans sa question, suggérant qu'ils sont sous Linux ou Mac.
Ian Roberts
5
Si les options sont mutuellement exclusives, la ligne de commande doit afficher un avertissement lorsque les deux sont utilisés. Qui a le temps pour cette anecdote?!
JJS
22

Lorsque l' -jaroption est utilisée, l' -cpoption est ignorée. Le seul moyen de définir le chemin de classe consiste à utiliser le fichier manifeste dans le fichier jar.

Il est plus facile d'utiliser simplement l' -cpoption, d'y ajouter votre fichier jar, puis d'appeler explicitement la classe principale.

En outre, en supposant que le /home/user/java/MyProgram/jar/libsdossier contient des fichiers jar (par opposition aux fichiers de classe), cela ne fonctionnera pas. Vous ne pouvez pas spécifier un dossier de fichier jar mais devez spécifier chaque fichier jar individuellement dans le chemin de classe (il vaut la peine d'écrire un simple script shell pour le faire pour vous s'il y a un nombre important de jars).

Jonathan
la source
0

C'est un peu délicat. Le script suivant est une tentative d'obtenir le chemin de classe à partir du manifeste du fichier jar, puis de permettre d'ajouter des entrées de chemin de classe supplémentaires. J'ai eu des résultats mitigés avec cela, mais je veux néanmoins partager le script afin qu'il puisse être rendu pleinement fonctionnel ici.

Le script a deux noms

  • showmanifest
  • calljar

en liant en dur les deux fichiers avec

ln calljar showmanifest

avec calljar -h, vous pouvez voir l'utilisation.

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

Pour des tests rapides et ponctuels d'une application, vous pouvez simplement créer un lien symbolique entre les fichiers JAR de dépendance nécessaires dans le répertoire contenant le fichier JAR principal de l'application.

Exemple (pour une application app.jarqui utilise la bibliothèque Eclipse SWT, qui dans mon cas a été installée dans /usr/share/java):

$ ln -s /usr/share/java/swt.jar .
$ java -jar app.jar
accuser
la source