109 votes

Appelez "java -jar MonFichier.jar" avec une option de classpath supplémentaire

J'ai créé un fichier jar contenant tous mes éléments compilés. De plus, mon ant build script copie les librairies nécessaires dans un sous-dossier "libs". La structure ressemble à ceci :

MyProgram.jar
libs/

Donc, lorsque j'essaie 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 se produit-il ?

170voto

Ian Roberts Points 59836

Vous utilisez soit -jar o -cp vous ne pouvez pas combiner les deux. Si vous souhaitez placer des JAR supplémentaires dans le classpath, vous devez soit les placer dans le manifeste du JAR principal, puis utiliser la commande java -jar ou vous mettez le classpath complet (incluant le JAR principal et ses dépendances) dans -cp et nommer la classe principale explicitement sur la ligne de commande

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

(J'utilise le dir/* qui indique à la java pour ajouter tous les .jar d'un répertoire particulier dans le classpath. Notez que l'option * 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 l'option de ant <manifestclasspath> tâche après en copiant les dépendances mais avant 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 ça en place, java -jar MyProgram.jar fonctionnera correctement, et inclura tous les libs JAR sur le classpath également.

28voto

Jonathan Points 1458

Lorsque le -jar est utilisée, l'option -cp est ignorée. La seule façon de définir le classpath est d'utiliser le fichier manifest dans le jar.

Il est plus facile d'utiliser l'option -cp ajoutez-y votre fichier jar, puis appelez explicitement la classe principale.

En outre, en supposant que le /home/user/java/MyProgram/jar/libs 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 vous devez spécifier chaque fichier jar individuellement dans le classpath (cela vaut la peine d'écrire un simple shell script pour faire cela pour vous s'il y a un nombre significatif de jars).

0voto

Wolfgang Fahl Points 1920

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

Le script a deux noms

  • showmanifest
  • calljar

en liant les deux fichiers ensemble 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

-1voto

ack Points 582

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

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X