Llame a "java -jar MyFile.jar" con la opción classpath adicional

91

Creé un archivo jar que contiene todas mis cosas compiladas. Además, mi script de compilación de hormigas copia las bibliotecas necesarias en una subcarpeta "libs". La estructura se ve así:

MyProgram.jar
libs/

Entonces, cuando intento ejecutar mi programa ahora, aparece el siguiente 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)

¿Por qué pasó esto?

toom
fuente

Respuestas:

151

Se utiliza ya sea -jar o -cp , no es posible combinar los dos. Si desea colocar JAR adicionales en la ruta de clases, debe colocarlos en el manifiesto del JAR principal y luego usar java -jaro colocar la ruta de clases completa (incluido el JAR principal y sus dependencias) -cpy nombrar la clase principal explícitamente en la línea de comando.

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

(Estoy usando la dir/*sintaxis que le dice al javacomando que agregue todos los .jararchivos de un directorio en particular a la ruta de clase. Tenga en cuenta que *el shell debe protegerlo de la expansión, por lo que he usado comillas simples).

Mencionas que estás usando Ant, por lo que para el enfoque de manifiesto alternativo, puedes usar la <manifestclasspath>tarea de ant después de copiar las dependencias pero antes de construir el 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 esto en su lugar, java -jar MyProgram.jarfuncionará correctamente e incluirá todos los libsarchivos JAR en la ruta de clase también.

Ian Roberts
fuente
Agregando lo anterior, O piense en agregar las entradas jar requeridas en el archivo MANIFEST.MF.
Himanshu Bhardwaj
@HimanshuBhardwaj de hecho, agregué un ejemplo de cómo hacerlo usando<manifestclasspath>
Ian Roberts
¿Qué pasa con el ':' en el 'MyProgram.jar: libs / *' es esto un separador?
Gobliins
@Gobliins dos puntos es el separador entre elementos en una ruta en los sistemas operativos Linux y Mac. En Windows, usaría punto y coma ( ;) en su lugar, ya que en Windows se usan dos puntos para las letras de unidad. El OP utilizó dos puntos y barras diagonales en su pregunta, lo que sugiere que están en Linux o Mac.
Ian Roberts
5
Si las opciones son mutuamente excluyentes, la línea de comando debe imprimir una advertencia cuando se utilizan ambas. ¿Quién tiene tiempo para esta trivia?
JJS
22

Cuando -jarse utiliza la -cpopción, la opción se ignora. La única forma de establecer la ruta de clase es utilizando el archivo de manifiesto en el jar.

Es más fácil usar la -cpopción, agregar su archivo jar a eso y luego llamar explícitamente a la clase principal.

Además, suponiendo que la /home/user/java/MyProgram/jar/libscarpeta contiene archivos jar (a diferencia de los archivos de clase), esto no funcionará. No puede especificar una carpeta de archivo jar, pero debe especificar cada archivo jar individualmente en la ruta de clase (vale la pena escribir un script de shell simple para hacer esto por usted si hay una cantidad significativa de jar).

Jonathan
fuente
0

Es un poco complicado. El siguiente script es un intento de obtener la ruta de clases del manifiesto del jar y luego permitir agregar entradas de ruta de clases adicionales. Obtuve resultados mixtos con esto, pero quiero compartir el script de todos modos para que pueda ser completamente funcional aquí.

El guión tiene dos nombres

  • showmanifest
  • calljar

al vincular los dos archivos junto con

ln calljar showmanifest

con calljar -h puede ver el uso.

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

Para pruebas rápidas y únicas de una aplicación, simplemente puede vincular simbólicamente los archivos JAR de dependencia necesarios en el directorio que contiene el archivo JAR de la aplicación principal.

Ejemplo (para una aplicación app.jarque usa la biblioteca Eclipse SWT, que en mi caso se instaló en /usr/share/java):

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