37 votes

Inclusion de fichiers JAR externes dans une nouvelle version de fichier JAR avec Ant

Je viens de 'hérité' Java-projet et de ne pas venir à partir d'un Java-fond, je suis un peu perdu à la fois. Eclipse est utilisé pour le débogage et l'exécution de l'application en cours de développement. J'ai par Eclipse a réussi à créer une .jar-file qui "comprend" toutes les externes nécessaires pots comme Log4J, xmlrpc-server, etc. Cette grosse .jar peut être exécutée avec succès à l'aide de:

java -jar myjar.jar

Ma prochaine étape est d'automatiser la génération à l'aide de Ant (version 1.7.1) donc je n'ai pas d'impliquer Eclipse pour faire des builds et de déploiement. Cela s'est avéré être un défi en raison de mon manque de java-connaissance. La racine du projet ressemble à ceci:

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

Mon build.xml contient les éléments suivants:

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

Je puis exécutez: ant build propre-jar

et un fichier nommé seraph.jar est placé dans le java/bin/jar-répertoire. Je puis essayez d'exécuter ce pot à l'aide de la commande suivante: java-jar bin/jar/seraph.jar

Le résultat est cette sortie dans la console:

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.

Je soupçonne que j'ai fait quelque chose d'incroyablement stupide dans la construction.fichier xml, et ont passé la meilleure partie de deux jours à essayer de variations sur la configuration, en vain. Toute aide sur l'obtention de ce travail est grandement apprécié.

Oh, et je suis désolé si j'ai laissé des informations cruciales sur. C'est ma première fois de poster ici, DONC.

49voto

Grodriguez Points 9945

À partir de votre ant buildfile, je suppose que ce que vous voulez est de créer une seule archive JAR qui contient non seulement vos classes de l'application, mais aussi le contenu des autres jar requis par votre application.

Toutefois, votre build-jar le fichier est seulement nécessaire de mettre les Pots à l'intérieur de votre propre POT; cela ne fonctionnera pas comme il est expliqué ici (voir la note).

Essayez de modifier ce:

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

pour cela:

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

Plus puissant et flexible de solutions sont les JarJar ou Un Pot de projets. Avoir un coup d'oeil dans ces cas ci-dessus ne répond pas à vos exigences.

15voto

Christopher Points 769

Avec les conseils utiles des personnes qui ont répondu ici, j'ai commencé à creuser dans One-Jar. Après quelques impasses (et des résultats identiques à ceux de mes précédents), j’ai réussi à le faire fonctionner. Pour d’autres références, j’énumère le fichier build.xml qui a fonctionné pour moi.

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

J'espère que quelqu'un d'autre peut en bénéficier.

2voto

Julian Simpson Points 304

Cheesle est droit. Il n'y a aucun moyen pour le chargeur de classe pour trouver l'embedded pots. Si vous mettez assez de commandes de débogage sur la ligne de commande, vous devriez être capable de voir le "java" échec d'une commande à ajouter les pots d'un classpath

Ce que vous voulez faire est parfois appelé une "uberjar'. J'ai trouvé un pot comme un outil pour aider le faire, mais je n'ai pas essayé. Bien sûr, il ya beaucoup d'autres approches.

0voto

Cheesle Points 108

Deux options: référencer les nouveaux fichiers jar dans votre chemin de classe ou décompresser toutes les classes dans les fichiers jar englobés et re-charger le tout! Pour autant que je sache, l'emballage de pots dans des pots n'est pas recommandé et vous aurez toujours la classe exception non trouvée!

0voto

Tansir1 Points 1049

Comme Cheesle dit, vous pouvez décompresser et de votre bibliothèque et des Pots et des re-pot de tous avec la modification suivante.

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

Les fichiers Jar sont vraiment juste des fichiers zip avec un fichier de manifeste intégré. Vous pouvez extraire et reconditionner la dépendance Bocaux dans votre application fichier Jar.

http://ant.apache.org/manual/Tasks/zip.html "La Zip tâche prend également en charge la fusion de plusieurs fichiers zip dans le fichier zip. Ceci est possible via l'attribut src de toutes les filesets ou par l'aide de la imbriquée fileset zipgroupfileset."

Faire attention aux licences impliqués avec votre dépendance à l'libaries. La liaison de l'extérieur à une bibliothèque et y compris la bibliothèque de votre application sont très différents les choses légalement.

EDIT 1: Darn mon ralentir la frappe. Grodriguez me battre pour elle. :)

EDIT 2: Si vous décidez que vous ne pouvez pas inclure de vos dépendances dans votre application, vous devez spécifier dans votre Bocal du chemin de classe, soit à la ligne de commande au démarrage ou via le fichier Manifeste. Il y a une belle commande ANT pour gérer la mise en forme spéciale du chemin de classe dans un fichier Manifest pour vous.

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

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