9

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.

Community
  • 1
  • 1
ewok
  • 20,148
  • 51
  • 149
  • 254
  • @see http://one-jar.sourceforge.net/index.php?page=build-tools&file=ant-example. – Jayan Mar 26 '12 at 16:49
  • @jayan is it possible to do this without any external tools? ie by just using Ant and writing my own build.xml file? – ewok Mar 26 '12 at 16:52
  • There are questions/answers in SO about creating runnable jars using eclipse etc. See this http://stackoverflow.com/questions/502960/eclipse-how-to-build-an-executable-jar-with-external-jar. I prefer one-jar as it creates a single runnable file. Intgrating to ant should take 10 minute. Then why not try and see the result? – Jayan Mar 26 '12 at 17:00
  • `Class-Path` takes a list of filenames as documented. Not a directory. –  Mar 26 '12 at 17:06
  • @jayan exporting to an executable jar using eclipse is not what I am trying to do. I need to generate an executable jar using Ant. The ultimate goal is to be able to distribute the source and allow the user to execute the code simply by executing `ant` on the command line, then double clicking a jar file that gets generated. Additionally, I would prefer not to have to install an external tool other than Ant itself. I need to know how to do this using only Ant. – ewok Mar 26 '12 at 17:45
  • @a_horse_with_no_name can you post an example of how to do this? – ewok Mar 26 '12 at 17:46

4 Answers4

20

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>
Mike
  • 1,924
  • 14
  • 16
  • I made the edits recommended. A few things I had to change (there is no `${dist}` property, I think you meant `${jar.dir}`), but the build still returns the same error. – ewok Mar 26 '12 at 16:01
  • Please also include the directory layout of the jar file. I'm wondering if the rt is upset that your class is in the default package. – Mike Mar 26 '12 at 16:08
  • I moved `HttpController.java` to the `pack` package (src/pack) and adjusted the build.xml file to reflect that, but the same error occurs. – ewok Mar 26 '12 at 16:16
  • If you moved it to a package, then you need to put the files in a sub directory. You also need to change your source code, but I'm assuming you have done that. Please post the directory structure of the jar file when you get a moment. – Danation Mar 26 '12 at 16:55
  • I even tested it this time :). If you still have a problem, you should run the program from the command line to see what the error is. e.g. "java -jar Test.jar". When I run, I get a socketexception since I don't have any services listening to port 80 locally. – Mike Mar 26 '12 at 17:09
  • @Mike I still get the same error. I copy and pasted your build.xml file exactly (adding the missing `` tag, of course). Apache HTTP.jar contains all the necessary class files for the code I have implemented. – ewok Mar 26 '12 at 17:51
  • 1
    And when you run it from the command line, what is the exact message you get? – Mike Mar 26 '12 at 17:53
5

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>
  • I've edited my build.xml file to reflect this, but I still keep getting the no main class error. For Whatever reasono, it is not recognizing the main class I have defined – ewok Mar 26 '12 at 18:19
  • Does your main class really have no package? Because that's what `Main-Class: HttpController` defines. –  Mar 26 '12 at 18:59
  • I'll update the above code. originally it had no package, but I have added a package to it and still encounter the same problem – ewok Mar 26 '12 at 19:01
1

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.

Ian S.
  • 1,831
  • 9
  • 17
0

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.

Danation
  • 743
  • 8
  • 20
  • I know there is a problem with the manifest file, but I need to know how to reconfigure build.xml so that ant generates it properly. – ewok Mar 26 '12 at 15:45
  • 1
    @ewok: Post the contents of your manifest file as well as the directory structure of the .jar file in your original question. Once the problem is identified, you should have a better idea of where the problem in the build.xml is. – Danation Mar 26 '12 at 15:49