librerias externas ejecutar desde crear consola con archivos java eclipse ant jar

java - externas - Incluyendo archivos jar externos en una nueva compilación de archivos jar con Ant



ejecutar ant desde consola (8)

¡Dos opciones, ya sea hacer referencia a las nuevas jarras en su classpath o desempaquetar todas las clases en las jarras adjuntas y volver a jar todo! Por lo que sé, no se recomienda empaquetar frascos dentro de frascos y siempre tendrás la excepción de la clase no encontrada.

Acabo de ''heredar'' un proyecto de Java y no proviene de un fondo de Java. A veces estoy un poco perdido. Eclipse se utiliza para depurar y ejecutar la aplicación durante el desarrollo. He logrado a través de Eclipse crear un archivo .jar que ''incluye'' todos los archivos jar requeridos externos como Log4J, xmlrpc-server, etc. Este gran .jar se puede ejecutar con éxito usando:

java -jar myjar.jar

Mi próximo paso es automatizar las compilaciones usando Ant (versión 1.7.1), así no tengo que involucrar a Eclipse para hacer construcciones e implementación. Esto ha demostrado ser un desafío debido a mi falta de conocimiento de Java. La raíz del proyecto se ve así:

|-> jars (where external jars have been placed) |-> java | |-> bin (where the finished .class / .jars are placed) | |-> src (Where code lives) | |-> ++files like build.xml etc |-> sql (you guessed it; sql! )

Mi build.xml contiene lo siguiente:

<?xml version="1.0" encoding="UTF-8" standalone="no"?> <project basedir="." default="build" name="Seraph"> <property environment="env"/> <property name="debuglevel" value="source,lines,vars"/> <property name="target" value="1.6"/> <property name="source" value="1.6"/> <property name="build.dir" value="bin"/> <property name="src.dir" value="src"/> <property name="lib.dir" value="../jars"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="jar.dir" value="${build.dir}/jar"/> <property name="jar.file" value="${jar.dir}/seraph.jar"/> <property name="manifest.file" value="${jar.dir}/MANIFEST.MF"/> <property name="main.class" value="no.easyconnect.seraph.core.SeraphCore"/> <path id="external.jars"> <fileset dir="${lib.dir}" includes="**/*.jar"/> </path> <path id="project.classpath"> <pathelement location="${src.dir}"/> <path refid="external.jars" /> </path> <target name="init"> <mkdir dir="${build.dir}"/> <mkdir dir="${classes.dir}"/> <mkdir dir="${jar.dir}"/> <copy includeemptydirs="false" todir="${build.dir}"> <fileset dir="${src.dir}"> <exclude name="**/*.launch"/> <exclude name="**/*.java"/> </fileset> </copy> </target> <target name="clean"> <delete dir="${build.dir}"/> </target> <target name="cleanall" depends="clean"/> <target name="build" depends="init"> <echo message="${ant.project.name}: ${ant.file}"/> <javac debug="true" debuglevel="${debuglevel}" destdir="bin" source="${source}" target="${target}" classpathref="project.classpath"> <src path="${src.dir}"/> </javac> </target> <target name="build-jar" depends="build"> <delete file="${jar.file}" /> <delete file="${manifest.file}" /> <manifest file="${manifest.file}" > <attribute name="built-by" value="${user.name}" /> <attribute name="Main-Class" value="${main.class}" /> </manifest> <jar destfile="${jar.file}" basedir="${build.dir}" manifest="${manifest.file}"> <fileset dir="${classes.dir}" includes="**/*.class" /> <fileset dir="${lib.dir}" includes="**/*.jar" /> </jar> </target> </project>

Luego corro: ant clean build-jar

y un archivo llamado seraph.jar se coloca en el directorio java / bin / jar. Luego trato de ejecutar este jar usando el siguiente comando: java -jar bin / jar / seraph.jar

El resultado es esta salida en la consola:

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/Logger at no.easyconnect.seraph.core.SeraphCore.<clinit>(SeraphCore.java:23) Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Logger at java.net.URLClassLoader$1.run(URLClassLoader.java:200) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:307) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:252) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320) ... 1 more Could not find the main class: no.easyconnect.seraph.core.SeraphCore. Program will exit.

Sospecho que he hecho algo increíblemente tonto en el archivo build.xml y he pasado la mayor parte de los dos días probando variaciones en la configuración, sin ningún resultado. Cualquier ayuda para que esto funcione es muy apreciada.

Oh, y lo siento si dejé alguna información crucial. Esta es la primera vez que publico aquí en SO.


A partir de su archivo de compilación de hormigas, supongo que lo que desea es crear un único archivo JAR que contendrá no solo las clases de su aplicación, sino también los contenidos de otros archivos JAR requeridos por su aplicación.

Sin embargo, su archivo build-jar solo está poniendo JAR requeridos dentro de su propio JAR; esto no funcionará como se explica here (ver nota).

Intenta modificar esto:

<jar destfile="${jar.file}" basedir="${build.dir}" manifest="${manifest.file}"> <fileset dir="${classes.dir}" includes="**/*.class" /> <fileset dir="${lib.dir}" includes="**/*.jar" /> </jar>

a esto:

<jar destfile="${jar.file}" basedir="${build.dir}" manifest="${manifest.file}"> <fileset dir="${classes.dir}" includes="**/*.class" /> <zipgroupfileset dir="${lib.dir}" includes="**/*.jar" /> </jar>

Soluciones más flexibles y potentes son los proyectos JarJar o One-Jar . Eche un vistazo a aquellos si lo anterior no satisface sus requisitos.


Como dijo Cheesle, puedes desempaquetar tus tarros de biblioteca y volver a jarlos con la siguiente modificación.

<jar destfile="${jar.file}" basedir="${build.dir}" manifest="${manifest.file}"> <fileset dir="${classes.dir}" includes="**/*.class" /> <zipgroupfileset dir="${lib.dir}" includes="**/*.jar" /> </jar>

Los archivos Jar son en realidad solo archivos zip con un archivo de manifiesto incrustado. Puede extraer y volver a empaquetar los Jars de dependencia en el archivo Jar de su aplicación.

http://ant.apache.org/manual/Tasks/zip.html "La tarea Zip también admite la fusión de varios archivos zip en el archivo zip. Esto es posible a través del atributo src de cualquier conjunto de archivos anidados o mediante el uso de conjunto de archivos anidados especiales zipgroupfileset ".

Presta atención a las licencias involucradas con tus bibliotecas de dependencia. Vincular externamente a una biblioteca e incluir la biblioteca en su aplicación son cosas muy diferentes legalmente.

EDIT 1: Darn mi tipado lento. Grodriguez me ganó. :)

EDIT 2: Si decide que no puede incluir sus dependencias en su aplicación, debe especificarlas en el classpath de Jar, ya sea en la línea de comando al inicio o mediante el archivo Manifest. Hay un buen comando en ANT para manejar el formato especial del classpath en un archivo Manifest para usted.

<manifestclasspath property="manifest.classpath" jarfile="${jar.file}"> <classpath location="${lib.dir}" /> </manifestclasspath> <manifest file="${manifest.file}" > <attribute name="built-by" value="${user.name}" /> <attribute name="Main-Class" value="${main.class}" /> <attribute name="Class-Path" value="${manifest.classpath}" /> </manifest>


Con el consejo útil de las personas que han respondido aquí, comencé a indagar en One-Jar. Después de algunos callejones sin salida (y algunos resultados que eran exactamente como mis resultados previos logré ponerlo en funcionamiento. Para referencia de otras personas estoy enumerando el build.xml que funcionó para mí.

<?xml version="1.0" encoding="UTF-8" standalone="no"?> <project basedir="." default="build" name="<INSERT_PROJECT_NAME_HERE>"> <property environment="env"/> <property name="debuglevel" value="source,lines,vars"/> <property name="target" value="1.6"/> <property name="source" value="1.6"/> <property name="one-jar.dist.dir" value="../onejar"/> <import file="${one-jar.dist.dir}/one-jar-ant-task.xml" optional="true" /> <property name="src.dir" value="src"/> <property name="bin.dir" value="bin"/> <property name="build.dir" value="build"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="jar.target.dir" value="${build.dir}/jars"/> <property name="external.lib.dir" value="../jars"/> <property name="final.jar" value="${bin.dir}/<INSERT_NAME_OF_FINAL_JAR_HERE>"/> <property name="main.class" value="<INSERT_MAIN_CLASS_HERE>"/> <path id="project.classpath"> <fileset dir="${external.lib.dir}"> <include name="*.jar"/> </fileset> </path> <target name="init"> <mkdir dir="${bin.dir}"/> <mkdir dir="${build.dir}"/> <mkdir dir="${classes.dir}"/> <mkdir dir="${jar.target.dir}"/> <copy includeemptydirs="false" todir="${classes.dir}"> <fileset dir="${src.dir}"> <exclude name="**/*.launch"/> <exclude name="**/*.java"/> </fileset> </copy> </target> <target name="clean"> <delete dir="${build.dir}"/> <delete dir="${bin.dir}"/> </target> <target name="cleanall" depends="clean"/> <target name="build" depends="init"> <echo message="${ant.project.name}: ${ant.file}"/> <javac debug="true" debuglevel="${debuglevel}" destdir="${classes.dir}" source="${source}" target="${target}"> <src path="${src.dir}"/> <classpath refid="project.classpath"/> </javac> </target> <target name="build-jar" depends="build"> <delete file="${final.jar}" /> <one-jar destfile="${final.jar}" onejarmainclass="${main.class}"> <main> <fileset dir="${classes.dir}"/> </main> <lib> <fileset dir="${external.lib.dir}" /> </lib> </one-jar> </target> </project>

Espero que alguien más pueda beneficiarse de esto.


Este es un problema de classpath al ejecutar un jar ejecutable de la siguiente manera:

java -jar myfile.jar

Una forma de solucionar el problema es establecer el classpath en la línea de comando java de la siguiente manera, agregando el jar faltante log4j:

java -cp myfile.jar:log4j.jar:otherjar.jar com.abc.xyz.MyMainClass

Por supuesto, la mejor solución es agregar el classpath en el manifiesto jar para que podamos usar la opción java "-jar":

<jar jarfile="myfile.jar"> .. .. <manifest> <attribute name="Main-Class" value="com.abc.xyz.MyMainClass"/> <attribute name="Class-Path" value="log4j.jar otherjar.jar"/> </manifest> </jar>

La siguiente respuesta demuestra cómo puede usar el manifestclasspath classpath para automatizar el inicio de la entrada del manifiesto classpath

No se puede encontrar la clase principal en el archivo compilado con Ant


Estoy usando NetBeans y necesitaba una solución para esto también. Después de googlear y comenzar a partir de la respuesta de Christopher, logré construir un script que te ayuda a hacer esto fácilmente en NetBeans. Estoy poniendo las instrucciones aquí en caso de que alguien más las necesite.

Lo que tienes que hacer es descargar one-jar. Puede usar el enlace desde aquí: http://one-jar.sourceforge.net/index.php?page=getting-started&file=ant Extraiga el archivo jar y busque la carpeta one-jar / dist que contiene one-jar- ant-task- .jar, one-jar-ant-task.xml y one-jar-boot- .jar. Extraiga o cópielos en una ruta que añadiremos a la secuencia de comandos siguiente, como el valor de la propiedad one-jar.dist.dir.

Simplemente copie la siguiente secuencia de comandos al final de su secuencia de comandos build.xml (de su proyecto NetBeans), justo antes de / project tag, reemplace el valor de one-jar.dist.dir con la ruta correcta y ejecute el objetivo one-jar.

Para aquellos de ustedes que no están familiarizados con la ejecución de objetivos, este tutorial podría ayudar: http://www.oracle.com/technetwork/articles/javase/index-139904.html . También le muestra cómo colocar las fuentes en un recipiente, pero se explotan, no se comprimen en recipientes.

<property name="one-jar.dist.dir" value="path/to/one-jar-ant"/> <import file="${one-jar.dist.dir}/one-jar-ant-task.xml" optional="true" /> <target name="one-jar" depends="jar"> <property name="debuglevel" value="source,lines,vars"/> <property name="target" value="1.6"/> <property name="source" value="1.6"/> <property name="src.dir" value="src"/> <property name="bin.dir" value="bin"/> <property name="build.dir" value="build"/> <property name="dist.dir" value="dist"/> <property name="external.lib.dir" value="${dist.dir}/lib"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="jar.target.dir" value="${build.dir}/jars"/> <property name="final.jar" value="${dist.dir}/${ant.project.name}.jar"/> <property name="main.class" value="${main.class}"/> <path id="project.classpath"> <fileset dir="${external.lib.dir}"> <include name="*.jar"/> </fileset> </path> <mkdir dir="${bin.dir}"/> <!-- <mkdir dir="${build.dir}"/> --> <!-- <mkdir dir="${classes.dir}"/> --> <mkdir dir="${jar.target.dir}"/> <copy includeemptydirs="false" todir="${classes.dir}"> <fileset dir="${src.dir}"> <exclude name="**/*.launch"/> <exclude name="**/*.java"/> </fileset> </copy> <!-- <echo message="${ant.project.name}: ${ant.file}"/> --> <javac debug="true" debuglevel="${debuglevel}" destdir="${classes.dir}" source="${source}" target="${target}"> <src path="${src.dir}"/> <classpath refid="project.classpath"/> </javac> <delete file="${final.jar}" /> <one-jar destfile="${final.jar}" onejarmainclass="${main.class}"> <main> <fileset dir="${classes.dir}"/> </main> <lib> <fileset dir="${external.lib.dir}" /> </lib> </one-jar> <delete dir="${jar.target.dir}"/> <delete dir="${bin.dir}"/> <delete dir="${external.lib.dir}"/> </target>

La mejor de las suertes y no te olvides de votar si te ayudó.


Puede usar un poco de funcionalidad que ya está incorporada en la tarea ant jar.

Si va a la documentación de la tarea ant jar y se desplaza hacia la sección "fusionar archivos", hay un fragmento para incluir todos los archivos * .class de todos los archivos jar del directorio "lib / main":

<jar destfile="build/main/checksites.jar"> <fileset dir="build/main/classes"/> <restrict> <name name="**/*.class"/> <archives> <zips> <fileset dir="lib/main" includes="**/*.jar"/> </zips> </archives> </restrict> <manifest> <attribute name="Main-Class" value="com.acme.checksites.Main"/> </manifest> </jar>

Esto crea un archivo jar ejecutable con una clase principal "com.acme.checksites.Main" e incorpora todas las clases de todos los jar en lib / main.

No hará nada inteligente en caso de conflictos en el espacio de nombres, duplicados y cosas por el estilo. Además, incluirá todos los archivos de clase, también los que no use, por lo que el archivo jar combinado será de tamaño completo.

Si necesita cosas más avanzadas como esa, eche un vistazo a los enlaces One-Jar y jar jar


Cheesle tiene razón. No hay forma de que el cargador de clases encuentre los archivos jar integrados. Si coloca suficientes comandos de depuración en la línea de comandos, debería poder ver que el comando ''java'' no puede agregar los archivos jar a un classpath

Lo que quieres hacer a veces se llama ''uberjar''. Encontré One-Jar como una herramienta para ayudar a hacerlos, pero no lo he intentado. Claro que hay muchos otros enfoques.