如何使用Ant创建一个捆绑的可运行jar文件

9
我查看了这个问题,但它并没有真正解决我的问题,所以我想发布一个新的问题。
我需要使用Ant创建可运行的JAR文件(通过双击运行)。我有以下Java代码和build.xml文件,可以很好地编译代码并创建一个jar文件,但是当我尝试通过双击运行该jar时,会出现一条消息,显示“找不到主类:HttpController.java”。
我怀疑我的问题与加载外部的Apache Http.jar有关,因为我已经成功地构建和运行了一个项目的jar,该项目与此项目完全相同,只是它没有引用任何外部jar。
以下是我的代码:
HttpController.java:
package pack;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpMessage;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

public class HttpController {
        public static void main(String[] args) {

        DefaultHttpClient client = new DefaultHttpClient();
        HttpHost httphost = new HttpHost("localhost", 80);

        try {

            HttpMessage req = new HttpGet("/test.html");
            HttpResponse resp = client.execute(httphost, (HttpGet) req);
            HttpEntity entity = resp.getEntity();

            BufferedReader in = new BufferedReader(new InputStreamReader(
                    entity.getContent()));

            String line = null;
            while ((line = in.readLine()) != null) {
                System.out.println(line);
            }

        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // shutdown the connection
            client.getConnectionManager().shutdown();
        }
    }
}

build.xml:

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <project name="Test" basedir="." default="jar">
    <property name="source.dir"     value="src"/>
    <property name="lib.dir"        value="lib"/>
    <property name="class.dir"      value="bin"/>
    <property name="jar.dir"        value="dist"/>
    <property name="main-class"     value="pack.HttpController"/>

    <path id="libraries.path">    
        <fileset dir="${lib.dir}">
            <include name="*.jar"/>
        </fileset>
    </path>

    <target name="clean" description="delete old files">
        <delete dir="${class.dir}"/>
        <delete dir="${jar.dir}"/>
    </target>

    <target name="compile" description="build class files" depends="clean">
        <mkdir dir="${class.dir}"/>
        <javac srcdir="${source.dir}" destdir="${class.dir}">
            <classpath refid="libraries.path"/>
        </javac>
    </target>

    <target name="jar" depends="compile">
        <mkdir dir="${jar.dir}"/>
        <mkdir dir="${jar.dir}/${lib.dir}"/>
        <copy todir="${jar.dir}/${lib.dir}" flatten="true">
            <path refid="libraries.path"/>
        </copy>
        <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${class.dir}">
            <manifest>
                <attribute name="Main-Class" value="${main-class}"/>
                <attribute name="Class-Path" value="${jar.dir}/${lib.dir}/Apache HTTP.jar"/>
            </manifest>
        </jar>  
    </target>

    <target name="run" depends="jar">
        <java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
    </target>
</project>

MANIFEST.MF:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.3
Created-By: 1.6.0_31-b05 (Sun Microsystems Inc.)
Main-Class: HttpController
Class-Path: dist/lib

编辑 根据Mike的回答,build.xml已更新。问题仍未解决。还按照Danation的回答发布了清单文件的内容。


@jayan,不使用任何外部工具,只使用Ant并编写自己的build.xml文件,是否有可能实现这一点? - ewok
SO 上有关于如何使用 Eclipse 等创建可运行 jar 文件的问题和答案。请参考此链接 https://dev59.com/D3RB5IYBdhLWcg3wz6ad。我更喜欢使用 One-jar,因为它可以创建一个单独的可运行文件。将其集成到 Ant 中只需要 10 分钟。那么为什么不尝试一下,看看结果如何呢? - Jayan
Class-Path 接受文件名列表,如所述文档。不接受目录。 - user330315
@jayan 使用Eclipse导出可执行jar文件不是我想要做的。我需要使用Ant生成可执行的jar文件。最终目标是能够分发源代码并允许用户通过在命令行上执行ant,然后双击生成的jar文件来执行代码。此外,我希望除了Ant本身之外不必安装其他外部工具。我需要知道如何仅使用Ant来完成这个任务。 - ewok
@a_horse_with_no_name,你能发一个如何做到这一点的例子吗? - ewok
4个回答

20

我重新调整了你的build.xml文件,以正确地将库包含在jar文件和Manifest类路径中。我假设你的"apache http.jar"文件是Apache Core的包装器,并且其中包含其他几个jar文件,用于Apache客户端等。

build.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="Test" basedir="." default="jar">
    <property name="source.dir"     value="src"/>
    <property name="lib.dir"        value="lib"/>
    <property name="class.dir"      value="bin"/>
    <property name="jar.dir"        value="dist"/>
    <property name="jar.file"        value="${jar.dir}/${ant.project.name}.jar"/>
    <property name="main-class"     value="pack.HttpController"/>

    <path id="libraries.path">    
        <fileset dir="${lib.dir}">
            <include name="*.jar"/>
        </fileset>
    </path>

    <target name="clean" description="delete old files">
        <delete dir="${class.dir}"/>
        <delete dir="${jar.dir}"/>
    </target>

    <target name="compile" description="build class files" depends="clean">
        <mkdir dir="${class.dir}"/>
        <javac srcdir="${source.dir}" destdir="${class.dir}">
            <classpath refid="libraries.path"/>
        </javac>
    </target>

    <target name="jar" depends="compile">
        <mkdir dir="${jar.dir}"/>
        <mkdir dir="${class.dir}/${lib.dir}"/>
        <copy todir="${class.dir}/${lib.dir}" flatten="true">
            <path refid="libraries.path"/>
        </copy>

        <manifestclasspath property="manifest.classpath" jarfile="${jar.file}">
            <classpath refid="libraries.path"/>
        </manifestclasspath>

        <jar destfile="${jar.file}" basedir="${class.dir}">
            <manifest>
                <attribute name="Main-Class" value="${main-class}"/>
                <attribute name="Class-Path" value="${manifest.classpath}"/>
            </manifest>
        </jar>  
    </target>

    <target name="run" depends="jar">
        <java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
    </target>

</project>

我已经按照建议进行了编辑。有几个地方我不得不做出更改(${dist}属性不存在,我想你是指${jar.dir}),但构建仍然返回相同的错误。 - ewok
请提供jar文件的目录布局。我在想rt是否因为你的类在默认包中而感到不满。 - Mike
如果你将它移动到一个包中,那么你需要把文件放在一个子目录中。你还需要改变你的源代码,但我假设你已经做过了。请在方便的时候发布jar文件的目录结构。 - Danation
@Mike 我仍然遇到相同的错误。我完全复制并粘贴了您的build.xml文件(当然,添加了缺失的</project>标签)。Apache HTTP.jar包含了我已经实现的代码所需的所有必要类文件。 - ewok
1
当您从命令行运行它时,您得到的确切消息是什么? - Mike
显示剩余2条评论

5
您的Class-Path条目有误,您需要单独指定每个jar文件,不能指定包含.jar文件的目录(只能指定包含.class文件的目录)。
这在JAR文件规范中有记录,并在Java教程中得到了很好的解释。
如果您有两个库名为FirstLib.jarSeconLib.jar,则您的Class-Path条目应如下所示:
Class-Path: lib/FirstLib.jar lib/SecondLib.jar 

编辑:
在您的build.xml文件中,它应该是这样的:
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${class.dir}">
    <manifest>
        <attribute name="Main-Class" value="${main-class}"/>
        <attribute name="Class-Path" value="${lib.dir}/FirstLib.jar ${lib.dir}/SecondLib.jar"/>
    </manifest>
</jar>

我已经编辑了我的build.xml文件以反映这一点,但我仍然不断收到“没有主类”错误。出于某种原因,它无法识别我定义的主类。 - ewok
你的主类真的没有包吗?因为这就是“Main-Class: HttpController”的定义。 - user330315
我会更新上面的代码。原本它没有包,但我已经添加了一个包,仍然遇到了同样的问题。 - ewok

1

这更像是一种制作可执行JAR文件的通用解决方案,但这是我使用的代码:

<?xml version="1.0" encoding="UTF-8"?>

<!--
  Any words or attribute values which are capitalized and separated by
  underscores are places where you add the data.

  Example: PROJECT_NAME, enter your project name
-->

<project name="PROJECT_NAME" default="jar" basedir=".">
    <!-- source code package -->
    <property name="src" value="SOURCE_CODE_FOLDER"/>

    <!-- classes folder (will be deleted) -->
    <property name="cls" value="classes"/>

    <!-- the directory of the jar file -->
    <property name="jar.dir" value="JAR_FILE_DIRECTORY"/>

    <!-- the jar file itself -->
    <property name="jar.file" value="${jar.dir}/${ant.project.name}-launch.jar"/>

    <!-- the fully qualified name of the main class -->
    <property name="main" value="PATH.TO.MAIN_CLASS"/>

    <!-- initialization -->
    <target name="-init">

        <!-- the class folder* -->
        <mkdir dir="${cls}"/>

        <!-- the jar directory -->
        <mkdir dir="${jar.dir}"/>
    </target>

    <!-- compiling of files -->
    <target name="-post-init-comp" depends="-init">

        <!--
          turn all of the source java classes into class files
          located in the directory we made*
        -->
        <javac srcdir="${src}" destdir="${cls}"/>
    </target>

    <!-- creation of the jar file -->
    <target name="jar" depends="-post-init-comp">

        <!-- make the executable jar -->
        <jar destfile="${jar.file}" basedir="${cls}">
            <manifest>

                <attribute name="Manifest-Version" value="1.0"/>
                <attribute name="Ant-Version" value="Apache Ant 1.8.3"/>

                <attribute name="Main-Class" value="${main}"/>

                <!--
                  for some reason, this is needed for me in order
                  to have the jar function right
                -->
                <attribute name="Class-Path" value="${main}"/>

                <!-- Any other mainfest data is added here -->

            </manifest>
        </jar>

        <!-- remove the class folder -->
        <delete dir="${cls}"/>
    </target>
</project>

我已经使用这个结构来制作许多不同的可执行jar文件,而且它比你之前用的更简单 :-)
在jar标签下放置在清单标签中的所有数据都将成为一个自动生成的清单文件,其中包含给定的数据。

0

请查看您的清单文件,问题很可能就在那里。我只在清单文件不正确时才会看到这个错误。

只是猜测,但我认为它试图将“.java”文件作为其主类运行,这是行不通的。

请参阅Oracle教程:http://docs.oracle.com/javase/tutorial/deployment/jar/appman.html

如果这完全没有帮助,请在原始问题中发布您的清单文件内容以及.jar文件的目录结构。


我知道清单文件存在问题,但我需要知道如何重新配置 build.xml,以便 ant 正确生成它。 - ewok
1
@ewok:请在您的原始问题中发布清单文件的内容以及.jar文件的目录结构。一旦问题被确定,您应该更好地了解build.xml中的问题所在。 - Danation

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接