38 Stimmen

Einbindung externer jar-Dateien in einen neuen jar-Dateibau mit Ant

Ich habe gerade ein Java-Projekt "geerbt", und da ich nicht aus dem Java-Umfeld komme, bin ich manchmal ein wenig verloren. Eclipse wird zum Debuggen und Ausführen der Anwendung während der Entwicklung verwendet. Mit Hilfe von Eclipse ist es mir gelungen, eine .jar-Datei zu erstellen, die alle erforderlichen externen Jars wie Log4J, xmlrpc-server usw. "enthält". Diese große .jar-Datei kann dann erfolgreich mit ausgeführt werden:

java -jar myjar.jar

Mein nächster Schritt besteht darin, Builds mit Ant (Version 1.7.1) zu automatisieren, damit ich Eclipse nicht für Builds und Deployment einsetzen muss. Dies hat sich aufgrund meiner mangelnden Java-Kenntnisse als Herausforderung erwiesen. Die Wurzel des Projekts sieht wie folgt aus:

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

Meine build.xml enthält das Folgende:

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

Ich laufe dann: ant clean build-jar

und eine Datei mit dem Namen seraph.jar wird im java/bin/jar-Verzeichnis abgelegt. Ich versuche dann, diese Jar-Datei mit dem folgenden Befehl auszuführen: java -jar bin/jar/seraph.jar

Das Ergebnis ist diese Ausgabe auf der Konsole:

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.

Ich vermute, dass ich etwas erstaunlich dummes in der build.xml-Datei gemacht habe und habe den größten Teil von zwei Tagen damit verbracht, Variationen der Konfiguration auszuprobieren, ohne Erfolg. Jede Hilfe auf immer diese Arbeit ist sehr geschätzt.

Oh, und es tut mir leid, wenn ich einige wichtige Informationen ausgelassen habe. Dies ist mein erster Beitrag hier bei SO.

0 Stimmen

An Ihrer Stelle würde ich Maven 2 oder Maven 3 anstelle von Ant verwenden, aber auch das wäre eine Herausforderung.

0 Stimmen

Ich habe es geschafft, einen guten Weg zu finden, um das zu erreichen, was ich wollte. Siehe unten für die Details.

0voto

Mark O'Connor Punkte 74233

Dies ist ein Problem mit dem Klassenpfad, wenn eine ausführbare jar-Datei wie folgt ausgeführt wird:

java -jar myfile.jar

Eine Möglichkeit, das Problem zu beheben, besteht darin, den Klassenpfad in der Java-Befehlszeile wie folgt zu setzen und das fehlende log4j jar hinzuzufügen:

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

Die beste Lösung ist natürlich, den Klassenpfad in das jar-Manifest einzufügen, so dass wir die Option "-jar" java verwenden können:

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

Die folgende Antwort zeigt, wie Sie die manifestclasspath um das Setzen des Klassenpfad-Manifesteintrags zu automatisieren

Die Hauptklasse kann in der mit Ant kompilierten Datei nicht gefunden werden

0 Stimmen

Das ist nicht das, was der Auftraggeber offenbar will. Aus seiner build.xml-Datei sieht es so aus, als wolle er tatsächlich alle erforderlichen Jars in die von ihm erzeugte Jar-Datei aufnehmen.

0 Stimmen

Das ist richtig, Grodriguez. Mein Ziel ist es, dass unsere Benutzer nur eine Datei herunterladen und diese ausführen können. Dass alles in einem Archiv enthalten ist, macht es einfach.

0voto

Alex Burdusel Punkte 2800

Ich verwende NetBeans und brauchte auch dafür eine Lösung. Nachdem ich herumgegoogelt habe und von Christophers Antwort ausgegangen bin, ist es mir gelungen, ein Skript zu erstellen, mit dem man dies in NetBeans einfach tun kann. Ich stelle die Anleitung hier rein, falls jemand anderes sie brauchen sollte.

Sie müssen lediglich one-jar herunterladen. Sie können den Link von hier verwenden: http://one-jar.sourceforge.net/index.php?page=getting-started&file=ant Entpacken Sie das jar-Archiv und suchen Sie nach one-jar \dist Ordner, der ein-jar-ant-task- enthält .jar, one-jar-ant-task.xml und one-jar-boot- .jar. Extrahieren Sie sie oder kopieren Sie sie in einen Pfad, den wir dem Skript unten als Wert der Eigenschaft one-jar.dist.dir hinzufügen werden.

Kopieren Sie einfach das folgende Skript an das Ende Ihres build.xml-Skripts (aus Ihrem NetBeans-Projekt), kurz vor dem /project-Tag, ersetzen Sie den Wert für one-jar.dist.dir durch den richtigen Pfad und führen Sie one-jar target aus.

Für diejenigen unter Ihnen, die mit dem Ausführen von Zielen nicht vertraut sind, könnte diese Anleitung hilfreich sein: http://www.oracle.com/technetwork/articles/javase/index-139904.html . Es wird auch gezeigt, wie man Quellen in einem Glas unterbringt, aber sie werden explodiert und nicht in Gläser gepresst.

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

Viel Glück und vergessen Sie nicht, Ihre Stimme abzugeben, wenn es Ihnen geholfen hat.

0voto

s3v1 Punkte 2863

Sie können einige Funktionen nutzen, die bereits in die Ant-Jar-Aufgabe integriert sind.

Wenn Sie zu Die Dokumentation für die Aufgabe ant jar und scrollen Sie nach unten zum Abschnitt "merging archives". Dort finden Sie ein Snippet, mit dem Sie alle *.class-Dateien aus allen Jars in Ihr "lib/main"-Verzeichnis einbinden können:

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

Diese Erzeugt eine ausführbare Jar-Datei mit einer Hauptklasse "com.acme.checksites.Main" und bettet alle Klassen aus allen Jars in lib/main ein.

Im Falle von Namensraumkonflikten, Duplikaten und dergleichen wird es nichts Gescheites tun. Außerdem werden alle Klassendateien einbezogen, auch die, die Sie nicht verwenden, so dass die kombinierte jar-Datei die volle Größe hat.

Wenn Sie solche fortgeschrittenen Dinge benötigen, sehen Sie sich Folgendes an Ein-Glas y jar jar links

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X