简体   繁体   中英

how to create a bundled runnable jar using Ant

I looked at this question , but it didn't really solve my problem, so I figured I'd post a new one.

I need to create a runnable jar (runnable simply by double clicking) using Ant. I have the following java code and build.xml file, which compiles the code just fine and creates a jar file, but when I try to run the jar by double clicking, i get a message saying "Could not find main class: HttpController.java."

I have the suspicion that my problem has to do with loading the external Apache Http.jar , as I have successfully built and run a jar for a project that is identical, except that it does not reference any external jars.

Here is my code:

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

EDIT build.xml has been updated as per Mike's answer. Problem is still not solved. Also posted contents of manifest file, as per Danation's answer.

Snip...

I have reworked your build.xml file to properly include the libraries in the jar file and in the Manifest classpath. I'm assuming that your "apache http.jar" file is a wrapper for Apache Core, and contains several other jar files in it for the apache client, etc.

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>

Your Class-Path entry is wrong, you have to specify each jar file individually, you cannot specify a directory with .jar files (only directories with .class files)

This is documented in the JAR File specification and explained quite nicely in the Java tutorial

If you have two libraries name FirstLib.jar and SeconLib.jar your Class-Path entry should look like this:

Class-Path: lib/FirstLib.jar lib/SecondLib.jar 

Edit :
Inside your build.xml this would look like this:

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

This is more of a general solution for making executable jar files but this is the code I use:

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

I've used this structure to make many different executable jar files, plus it is simpler than what you had :-)

All data put in the manifest tags under the jar tags will become an automatically generated manifest file with the given data.

Take a look at your manifest file, it's most likely an issue in there. I've only seen that error when the manifest file is incorrect.

Just speculation, but I think it's trying to run a ".java" file as it's main class, which isn't going to work.

See the oracle tutorial: http://docs.oracle.com/javase/tutorial/deployment/jar/appman.html .

If that doesn't help at all, post the contents of your manifest file as well as the directory structure of the .jar file in your original question.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM