Classpath incluyendo JAR dentro de un JAR

134

¿Es posible especificar un Java classpathque incluya un archivo JAR contenido en otro archivo JAR?

Paul Reiners
fuente

Respuestas:

94

Si está intentando crear un solo contenedor que contenga su aplicación y sus bibliotecas requeridas, hay dos maneras (que yo sepa) de hacerlo. El primero es One-Jar , que utiliza un cargador de clases especial para permitir el anidamiento de jarras. El segundo es UberJar , (o Shade ), que explota las bibliotecas incluidas y coloca todas las clases en el jar de nivel superior.

También debo mencionar que UberJar y Shade son complementos para Maven1 y Maven2 respectivamente. Como se menciona a continuación, también puede usar el complemento de ensamblaje (que en realidad es mucho más potente, pero mucho más difícil de configurar correctamente).

Steve Moyer
fuente
81
Así que aquí estamos, 5 años después. Parece que esto sigue siendo cierto. Muy triste :(
T3rm1
3
La mejor manera que conozco hoy en día es usar el artefacto de jarra IntelliJ. Extrae todas las clases de los frascos dependientes y los coloca en su único frasco.
enl8enmentnow
2
Eso no es posible en algunas situaciones, como cuando se utilizan implementaciones de JCE como BouncyCastle que deben firmarse
debuti
1
@ BrainSlugs83 ... ¿Qué intentas hacer? Creo que el cargador de clases y el empaquetador de One-Jar sigue siendo la respuesta para incluir un Jar dentro de un proyecto Jar (para Java SE). El uso de UberJar elimina el problema al extraer archivos de clase en su Jar.
Steve Moyer
1
El enlace UberJar necesita credenciales de inicio de sesión. ¿Hay alguna página web sucesora?
Thomas Weller
50

NO desea utilizar esas soluciones de "explosión de contenido JAR". Definitivamente hacen que sea más difícil ver cosas (ya que todo explota al mismo nivel). Además, podría haber conflictos de nombres (no debería suceder si las personas usan paquetes adecuados, pero no siempre se puede controlar esto).

La característica que desea es una de las 25 principales RFE de Sun : RFE 4648386 , que Sun, en su sabiduría infinita, ha designado como de baja prioridad. Solo podemos esperar que el Sol se despierte ...

Mientras tanto, la mejor solución que he encontrado (que deseo que Sun copie en el JDK) es usar el cargador de clases personalizado JarClassLoader .

Bruce Willis
fuente
44
Los conflictos de nomenclatura están casi garantizados con cosas como la configuración de log4j y los textos de licencia.
Michael Borgwardt
1
Lamentablemente, JarClassLoader es GPLv3 / commercial, por lo que probablemente no va a ser "copiado por el sol (ahora oráculo)", y no puede usarse comercialmente a menos que tenga la influencia política interna y el tiempo para comprar algo. Sin embargo, estoy de acuerdo en que las jarras que barf en el directorio actual son algo malo.
Gus
+1 para la idea en esta respuesta (aunque no haré +1 en la respuesta en sí): no puede volver a empaquetar ningún archivo JAR firmado, por lo que esta técnica no se puede utilizar en muchas situaciones (por ejemplo, Java activation.jar)
Christopher Schultz
31

Después de algunas investigaciones, he encontrado un método que no requiere Maven o cualquier extensión / programa de terceros.

Puede usar "Class-Path" en su archivo de manifiesto.

Por ejemplo:

Crear archivo de manifiesto MANIFEST.MF

Manifest-Version: 1.0
Created-By: Bundle
Class-Path: ./custom_lib.jar
Main-Class: YourMainClass

Compila todas tus clases y corre jar cfm Testing.jar MANIFEST.MF *.class custom_lib.jar

csignifica crear archivo findica que desea especificar el archivo ves para entrada detallada msignifica que pasaremos un archivo de manifiesto personalizado

Asegúrese de incluir lib en el paquete jar. Deberías poder ejecutar jar de la manera normal.

Residencia en: http://www.ibm.com/developerworks/library/j-5things6/

toda la otra información que necesita sobre la ruta de clase se encuentra aquí

Tezar
fuente
19
Esta respuesta no funciona. del oráculo doc (vinculado por banana arriba): "El encabezado Class-Path apunta a clases o archivos JAR en la red local, no a archivos JAR dentro del archivo JAR"
sebnukem
Cualquier cosa que se sepa si esto también se puede usar en una configuración de Android.
Colapso Mostowski
2
Funciona para el escenario jar ejecutable (probado), pero puede no funcionar cuando desea incluir un jar en un jar de biblioteca.
Cychih
55
Oh no, no funciona, una vez que me custom_lib.jaralejé, el frasco ya no se puede ejecutar :(
Cychih
¿Cómo se pueden especificar múltiples jarras en el Class-Path?
Abhishek Tiwari
22

Use la etiqueta zipgroupfileset (usa los mismos atributos que una etiqueta de conjunto de archivos ); Descomprimirá todos los archivos en el directorio y los agregará a su nuevo archivo. Más información: http://ant.apache.org/manual/Tasks/zip.html

Esta es una forma muy útil de solucionar el problema de jar-in-a-jar: lo sé porque busqué en Google esta pregunta exacta de StackOverflow al intentar averiguar qué hacer. Si desea empaquetar un frasco o una carpeta de frascos en su único frasco construido con Ant, entonces olvídese de todo este classpath o complementos de terceros, todo lo que tiene que hacer es esto (en Ant):

<jar destfile="your.jar" basedir="java/dir">
  ...
  <zipgroupfileset dir="dir/of/jars" />
</jar>
ecbrodie
fuente
8

Si está construyendo con ant (estoy usando ant from eclipse), puede agregar los archivos jar adicionales diciendo a ant para agregarlos ... No necesariamente es el mejor método si tiene un proyecto mantenido por varias personas pero funciona para proyecto de una persona y es fácil.

por ejemplo, mi objetivo que estaba creando el archivo .jar era:

<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
    <manifest>
        <attribute name="Author" value="ntg"/>
        ................................
        <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
    </manifest>
</jar>

Acabo de agregar una línea para hacerlo:

<jar ....">
    <zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
    <manifest>
        ................................
    </manifest>
</jar>

dónde

<property name="external-lib-dir" 
          value="C:\...\eclipseWorkspace\Filter\external\...\lib" />

fue el directorio con los frascos externos. Y eso es...

ntg
fuente
6

No sin escribir su propio cargador de clases. Puede agregar frascos al classpath del frasco, pero deben estar ubicados conjuntamente, no contenidos en el frasco principal.

Joe Skora
fuente
2

Debe crear un cargador de clases personalizado para hacer esto o una biblioteca de terceros que lo admita. Su mejor opción es extraer el frasco del tiempo de ejecución y agregarlos al classpath (o tenerlos ya agregados al classpath).

James Schek
fuente
2

Utilizo maven para mis compilaciones de java que tiene un complemento llamado complemento de ensamblaje de maven .

Hace lo que pides, pero como describen algunas de las otras sugerencias, básicamente explota todos los frascos dependientes y los recombina en un solo frasco

Vinnie
fuente
El complemento de ensamblaje de Maven es bastante doloroso de usar ... UberJar y Shade son complementos de Maven1 y Maven2 (un hecho que debería haber mencionado anteriormente, y lo haré ahora)
Steve Moyer
2

Si ha eclpise IDE, solo necesita exportar su JAR y elegir "Bibliotecas de paquetes requeridos en JAR generado". eclipse agregará automáticamente los JAR dependientes requeridos en el JAR generado, así como también generó un cargador de clases personalizado de eclipse que carga estos JAR automáticamente.

amralieg
fuente
1

Estaba a punto de recomendar extraer todos los archivos en el mismo nivel, y luego hacer un tarro con el resultado, ya que el sistema de paquetes debería mantenerlos perfectamente separados. Esa sería la forma manual, supongo que las herramientas indicadas por Steve lo harán muy bien.

PhiLho
fuente
1

Bueno, hay una manera muy fácil si estás usando Eclipse.

Exporte su proyecto como un archivo Jar "Ejecutable" (haga clic con el botón derecho en la carpeta del proyecto desde Eclipse, seleccione "Exportar ..."). Cuando configure las opciones de exportación, asegúrese de seleccionar "Extraer las bibliotecas necesarias en el Jar generado". Recuerde, seleccione "Extraer ..." y no "Empaquetar bibliotecas requeridas ...".

Además : debe seleccionar una configuración de ejecución en su configuración de exportación. Por lo tanto, siempre puede crear un main () vacío en alguna clase y usarlo para su configuración de ejecución.

De todos modos, no se garantiza que funcione el 100% del tiempo, ya que notará un mensaje emergente que le indica que se asegure de verificar las licencias de los archivos Jar que está incluyendo y algo sobre no copiar archivos de firma. Sin embargo, he estado haciendo esto durante años y nunca he encontrado un problema.

CM_2014
fuente
0

La extracción en un Uber-dir funciona para mí, ya que todos deberíamos usar root: \ java y tener código de salidas en paquetes con versiones. Es decir, ca.tecreations-1.0.0. La firma está bien porque los frascos están intactos desde su ubicación descargada. Firmas de terceros intactas, extraer a c: \ java. Ahí está mi proyecto dir. ejecutar desde el lanzador para que Java -cp c: \ java Launcher

Tim de Vries
fuente
Entonces frascos en c: \ java \ jars. Fuente, binarios y recursos en c: \ java. Excluir copias de seguridad, propiedades, keystore_private. Descomprima en c: \ java y ejecútelo con la herramienta de formación classpath, por lo que tal vez ca.tecreations.system.tools.SystemTool o una clase java comparable. Ejecuta eso.
Tim de Vries