Classpath, y compris JAR dans un JAR

134

Est-il possible de spécifier un Java classpathqui inclut un fichier JAR contenu dans un autre fichier JAR?

Paul Reiners
la source

Réponses:

94

Si vous essayez de créer un seul fichier jar contenant votre application et ses bibliothèques requises, il existe deux façons (à ma connaissance) de le faire. Le premier est One-Jar , qui utilise un chargeur de classe spécial pour permettre l'imbrication des fichiers JAR. Le second est UberJar , (ou Shade ), qui explose les bibliothèques incluses et place toutes les classes dans le fichier jar de niveau supérieur.

Je dois également mentionner que UberJar et Shade sont des plugins pour Maven1 et Maven2 respectivement. Comme mentionné ci-dessous, vous pouvez également utiliser le plugin d'assemblage (qui en réalité est beaucoup plus puissant, mais beaucoup plus difficile à configurer correctement).

Steve Moyer
la source
81
Nous voilà donc 5 ans plus tard. On dirait que c'est toujours vrai. Très triste :(
T3rm1
3
La meilleure façon que je connaisse de nos jours est d'utiliser l'artefact IntelliJ jar. Il extrait toutes les classes des pots dépendants et les met dans votre seul pot.
enl8enmentnow
2
Ce n'est pas possible dans certaines situations, comme lorsque vous utilisez des implémentations JCE comme BouncyCastle qui doivent être signées
début
1
@ BrainSlugs83 ... Qu'essayez-vous de faire? Je pense que le classloader et le packager de One-Jar sont toujours la solution pour inclure un Jar dans des projets Jar (pour Java SE). L'utilisation d'UberJar élimine le problème en extrayant les fichiers de classe dans votre Jar.
Steve Moyer
1
Le lien UberJar nécessite des informations de connexion. Y a-t-il une page Web qui lui succède?
Thomas Weller
50

Vous ne souhaitez PAS utiliser ces solutions «exploser le contenu JAR». Ils rendent définitivement plus difficile de voir les choses (puisque tout est explosé au même niveau). De plus, il peut y avoir des conflits de noms (cela ne devrait pas se produire si les gens utilisent des packages appropriés, mais vous ne pouvez pas toujours contrôler cela).

La fonctionnalité que vous recherchez est l'une des 25 meilleures RFE Sun : RFE 4648386 , que Sun, dans son infinie sagesse, a désignée comme étant de faible priorité. On ne peut qu'espérer que Sun se réveille ...

En attendant, la meilleure solution que j'ai rencontrée (que je souhaite que Sun copie dans le JDK) est d'utiliser le chargeur de classe personnalisé JarClassLoader .

Bruce Willis
la source
4
Les conflits de noms sont en fait presque garantis avec des choses comme la configuration de log4j et les textes de licence.
Michael Borgwardt
1
Malheureusement, JarClassLoader est GPLv3 / commercial donc il ne sera probablement pas "copié par sun (maintenant oracle)", et ne peut pas être utilisé commercialement à moins d'avoir le poids politique interne et le temps d'acheter quelque chose. Je conviens cependant que les bocaux qui barf sur le répertoire courant sont une mauvaise chose.
Gus
+1 pour l'idée dans cette réponse (même si je ne vais pas +1 pour la réponse elle-même): vous ne pouvez pas reconditionner un fichier JAR signé, donc cette technique ne peut pas être utilisée dans de nombreuses situations (par exemple, Java activation.jar).
Christopher Schultz
31

Après quelques recherches, j'ai trouvé une méthode qui ne nécessite pas maven ou une extension / programme tiers.

Vous pouvez utiliser "Class-Path" dans votre fichier manifeste.

Par exemple:

Créer le fichier manifeste MANIFEST.MF

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

Compilez toutes vos classes et exécutez jar cfm Testing.jar MANIFEST.MF *.class custom_lib.jar

csignifie créer une archive findique que vous voulez spécifier le fichier vest pour une entrée verbeuse msignifie que nous allons transmettre un fichier manifeste personnalisé

Assurez-vous que vous avez inclus lib dans le package jar. Vous devriez pouvoir exécuter jar de la manière habituelle.

basé sur: http://www.ibm.com/developerworks/library/j-5things6/

toutes les autres informations dont vous avez besoin sur le chemin des classes vous trouvez ici

Tezar
la source
19
Cette réponse ne fonctionne pas . à partir du document oracle (lié par la banane ci-dessus): "L'en-tête Class-Path pointe vers des classes ou des fichiers JAR sur le réseau local, pas des fichiers JAR dans le fichier JAR"
sebnukem
Tout ce qui sait si cela peut également être utilisé dans un paramètre Android.
Mostowski Collapse
2
Cela fonctionne pour le scénario jar exécutable (testé), mais cela peut ne pas fonctionner lorsque vous souhaitez inclure un fichier jar dans un fichier jar de bibliothèque.
Cychih
5
Oh non ça ne marche pas, une fois que j'ai déménagé custom_lib.jar, le pot ne peut plus être exécuté :(
Cychih
Comment plusieurs jars peuvent-ils être spécifiés dans le Class-Path?
Abhishek Tiwari
22

Utilisez la balise zipgroupfileset (utilise les mêmes attributs qu'une balise d'ensemble de fichiers ); il décompressera tous les fichiers du répertoire et les ajoutera à votre nouveau fichier d'archive. Plus d'informations: http://ant.apache.org/manual/Tasks/zip.html

C'est un moyen très utile de contourner le problème du jar-in-a-jar - je le sais parce que j'ai recherché sur Google cette question exacte de StackOverflow tout en essayant de savoir quoi faire. Si vous voulez empaqueter un jar ou un dossier de jars dans votre jar construit avec Ant, alors oubliez tout ce chemin de classe ou ce plugin tiers, tout ce que vous avez à faire est ceci (dans Ant):

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

Si vous construisez avec ant (j'utilise ant from eclipse), vous pouvez simplement ajouter les fichiers jar supplémentaires en disant à fourmi de les ajouter ... Ce n'est pas nécessairement la meilleure méthode si vous avez un projet géré par plusieurs personnes mais cela fonctionne pour un projet de personne et c'est facile.

par exemple, ma cible qui construisait le fichier .jar était:

<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>

Je viens d'ajouter une ligne pour le faire:

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

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

était le dir avec les pots externes. Et c'est tout...

ntg
la source
6

Pas sans écrire votre propre chargeur de classe. Vous pouvez ajouter des fichiers jar au chemin de classe du fichier jar, mais ils doivent être colocalisés et non contenus dans le fichier jar principal.

Joe Skora
la source
2

Pour ce faire, vous devez créer un chargeur de classe personnalisé ou une bibliothèque tierce qui prend en charge cela. Votre meilleur pari est d'extraire le fichier jar du runtime et de l'ajouter au classpath (ou de les ajouter déjà au classpath).

James Schek
la source
2

J'utilise maven pour mes builds java qui a un plugin appelé le plugin d'assemblage maven .

Il fait ce que vous demandez, mais comme certaines des autres suggestions le décrivent - en faisant exploser tous les pots dépendants et en les recombinant en un seul pot

Vinnie
la source
Le plugin d'assemblage Maven est assez pénible à utiliser ... UberJar et Shade sont des plugins Maven1 et Maven2 (un fait que j'aurais dû mentionner ci-dessus, et je le ferai maintenant)
Steve Moyer
2

Si vous avez eclpise IDE, il vous suffit d'exporter votre JAR et de choisir "Package Requis des bibliothèques dans le JAR généré". eclipse ajoutera automatiquement les JAR dépendants requis dans le JAR généré et générera également un chargeur de classe personnalisé eclipse qui chargera ces JAR automatiquement.

amralieg
la source
1

J'étais sur le point de conseiller d'extraire tous les fichiers au même niveau, puis de créer un pot à partir du résultat, car le système de paquetage devrait les garder soigneusement séparés. Ce serait la méthode manuelle, je suppose que les outils indiqués par Steve le feront très bien.

PhiLho
la source
1

Eh bien, il existe un moyen très simple si vous utilisez Eclipse.

Exportez votre projet en tant que fichier Jar "Runnable" (cliquez avec le bouton droit sur le dossier du projet depuis Eclipse, sélectionnez "Exporter ..."). Lorsque vous configurez les paramètres d'exportation, assurez-vous de sélectionner «Extraire les bibliothèques requises dans le fichier Jar généré». N'oubliez pas, sélectionnez "Extraire ..." et non "Package requis bibliothèques ...".

De plus : vous devez sélectionner une configuration d'exécution dans vos paramètres d'exportation. Ainsi, vous pouvez toujours créer un main () vide dans une classe et l'utiliser pour votre configuration d'exécution.

Quoi qu'il en soit, il n'est pas garanti de fonctionner à 100% du temps - comme vous remarquerez un message contextuel vous demandant de vous assurer de vérifier les licences des fichiers Jar que vous incluez et de ne pas copier les fichiers de signature. Cependant, je fais cela depuis des années et je n'ai jamais rencontré de problème.

CM_2014
la source
0

Extraire dans un Uber-dir fonctionne pour moi car nous devrions tous utiliser root: \ java et avoir du code de sortie dans des packages avec versioning. Ie ca.tecreations-1.0.0. La signature est correcte car les pots sont intacts depuis leur emplacement téléchargé. Signatures tierces intactes, extraire vers c: \ java. Il y a mon projet dir. exécuter à partir du lanceur donc java -cp c: \ java Launcher

Tim de Vries
la source
Donc jars dans c: \ java \ jars. Source, binaires et ressources dans c: \ java. Excluez les sauvegardes, les propriétés, keystore_private. Décompressez dans c: \ java et exécutez avec l'outil de formation de chemin de classe, donc peut-être ca.tecreations.system.tools.SystemTool ou une classe Java comparable. Exécutez cela.
Tim de Vries