Cannot find Main Class in File Compiled With Ant

11,348

Looks like your runtime classpath is missing the jar containing the class org.supercsv.io.ICsvBeanReader.

The gotcha is that you cannot set the classpath from the command-line when calling an executable jar. You have to set it within the manifest as follows:

<target name="dist" depends="compile" description="Generates distributable">
    <!-- creates the distribution directory -->
    <mkdir dir="${dist}/lib" />

    <!-- Remove manifest. This jar will end up on the classpath of CC.jar -->
    <jar jarfile="${dist}/lib/CC-${DSTAMP}.jar" basedir="${build}"/>

    <!-- Fancy task that takes the pain out creating properly formatted manifest value -->
    <manifestclasspath property="mf.classpath" jarfile="${dist}/lib/CC.jar">
        <classpath>
            <fileset dir="${dist}/lib" includes="*.jar"/>
        </classpath><!--end tag-->
    </manifestclasspath>

    <!-- This is the executable jar -->
    <jar jarfile="${dist}/lib/CC.jar" basedir="${build}">
        <manifest>
            <attribute name="Main-Class" value="jab.jm.main.Test"/>
            <attribute name="Class-Path" value="${mf.classpath}"/> 
        </manifest>
    </jar>

</target>

This approach will allow you to run the jar as follows:

java -jar CC.jar

Without the extra manifest entry you have to run the jar as follows:

java -cp CC.jar:CC-DSTAMPVALUE.jar jab.jm.main.Test

Note

Only the CC.jar is executable and needs the special manifest. Using this pattern means future additional jars, placed into the lib directory, will be automatically included in the run-time classpath. (Useful for open source dependencies like log4j)

Obviously, when running the CC.jar you'll get a similar error if the jar files are not present :-)

Share:
11,348
Justian Meyer
Author by

Justian Meyer

Graduated from Georgia Institute of Technology in Dec 2014 with degree focus on Information/Internetworks and Intelligence. Started my programming journey at 10 years old with DarkBasic and have since gone on to work at two top tech companies in Silicon Valley.

Updated on June 17, 2022

Comments

  • Justian Meyer
    Justian Meyer almost 2 years

    I compile and run my program in Eclipse and everything works fine, but when I package it with Ant and run it, I get this error:

    Exception in thread "main" java.lang.NoClassDefFoundError: org/supercsv/io/ICsvB
    eanReader
    Caused by: java.lang.ClassNotFoundException: org.supercsv.io.ICsvBeanReader
            at java.net.URLClassLoader$1.run(Unknown Source)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.net.URLClassLoader.findClass(Unknown Source)
            at java.lang.ClassLoader.loadClass(Unknown Source)
            at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
            at java.lang.ClassLoader.loadClass(Unknown Source)
    Could not find the main class: jab.jm.main.Test. Program will exit.
    

    Note that this is a runtime error and not a compiler error with Ant.

    I've built this project in the past with 0 issues and now it suddenly acts up on me when I add a second package to my lib folder?

    Here's the build file for reference:

    <?xml version="1.0" ?>
    
    <project name="ServerJar" default="dist" basedir=".">
     <description>
      Builds client files into .jar
     </description>
     <!-- [build variables] -->
     <property name="src" location="src" />
     <property name="build" location="build" />
     <property name="dist" location="dist" />
     <property name="lib" location="lib" />
     <!-- [path to packages] -->
     <path id="master-classpath">
         <fileset dir="${lib}">
             <include name="*.jar"/>
         </fileset>
     </path>
    
    
     <target name="init">
      <!-- makes time stamp to be used in jar name -->
      <tstamp />
      <!-- creates build directory structure -->
      <mkdir dir="${build}" />
     </target>
    
     <target name="compile" depends="init" description="Compiles the source">
      <!-- compiles the java code from ${src} into ${build} -->
      <!-- <javac srcdir="${src}" destdir="${build}" /> -->
      <javac destdir= "${build}">
          <src path="${src}"/>
          <classpath refid="master-classpath"/>
      </javac>
     </target>
    
     <target name="dist" depends="compile" description="Generates distributable">
      <!-- creates the distribution directory -->
      <mkdir dir="${dist}/lib" />
    
      <!-- puts everything in ${build} into the jar file -->
      <jar jarfile="${dist}/lib/CC-${DSTAMP}.jar" basedir="${build}">
       <manifest>
        <attribute name="Main-Class" value="jab.jm.main.Test" />
       </manifest>
      </jar>
    
      <!-- makes a jar file for quick test execution -->
      <jar jarfile="${dist}/lib/CC.jar" basedir="${build}">
       <manifest>
        <attribute name="Main-Class" value="jab.jm.main.Test" />
       </manifest>
      </jar>
     </target>
    
     <target name="clean" description="Cleans up the extra build files">
      <!-- deletes the ${build} and ${dist} directories -->
      <delete dir="${build}" />
      <delete dir="${dist}" />
     </target>
    </project>
    

    Thanks in advance for the help!

    EDIT:

    Here's what the construction for my main class looks like (this is not the actual file, but this is what I based mine on). The construction is very odd for a java program and might be giving Ant some issues. Any recommendations on how to reconstruct this? I got a bunch of errors when trying to separate this into multiple parts. I've just never seen a construction like this before (yes, I understand HOW it works (and it does when compiled), but Ant might not like it).

    import java.io.FileReader;
    import java.io.IOException;
    
    import org.supercsv.cellprocessor.Optional;
    import org.supercsv.cellprocessor.ParseDate;
    import org.supercsv.cellprocessor.ParseInt;
    import org.supercsv.cellprocessor.constraint.StrMinMax;
    import org.supercsv.cellprocessor.constraint.Unique;
    import org.supercsv.cellprocessor.ift.CellProcessor;
    import org.supercsv.io.CsvBeanReader;
    import org.supercsv.io.ICsvBeanReader;
    import org.supercsv.prefs.CsvPreference;
    
    class ReadingObjects {
        static final CellProcessor[] userProcessors = new CellProcessor[] {
            new Unique(new StrMinMax(5, 20)),
            new StrMinMax(8, 35),
            new ParseDate("dd/MM/yyyy"),
            new Optional(new ParseInt()),
            null
        };
    
        public static void main(String[] args) throws Exception {
            ICsvBeanReader inFile = new CsvBeanReader(new FileReader("foo.csv"), CsvPreference.EXCEL_PREFERENCE);
            try {
              final String[] header = inFile.getCSVHeader(true);
              UserBean user;
              while( (user = inFile.read(UserBean.class, header, userProcessors)) != null) {
                System.out.println(user.getZip());
              }
            } finally {
              inFile.close();
            }
       }
    }
    
    public class UserBean {
        String username, password, town;
        Date date;
        int zip;
    
        public Date getDate() {
            return date;
        }
        public String getPassword() {
            return password;
        }
        public String getTown() {
            return town;
        }
        public String getUsername() {
            return username;
        }
        public int getZip() {
            return zip;
        }
        public void setDate(final Date date) {
            this.date = date;
        }
        public void setPassword(final String password) {
            this.password = password;
        }
    
        public void setTown(final String town) {
            this.town = town;
        }
        public void setUsername(final String username) {
            this.username = username;
        }
        public void setZip(final int zip) {
            this.zip = zip;
        }
    }
    

    Notice how the class's name is actually UserBean and it contains a non-public class named ReadingObjects within it that holds the main method.

    • rancidfishbreath
      rancidfishbreath almost 14 years
      Can you run a 'jar -tf' on the jar that gets created by ant and post the results?
    • Romain Hippeau
      Romain Hippeau almost 14 years
      Can you explain " it suddenly acts up on me when I add a second package to my lib folder?"
    • Justian Meyer
      Justian Meyer almost 14 years
      @Rancidfishbreath: I just get:['jar' is not recognized as an internal or external command, operable program or batch file.] I can't find any solid solutions online, but I believe it has to do with adding the JDK to my Path variable? @Romain: I added a second .jar file to my lib folder and build path. That's when it started acting up. Everything was working fine before then. I may be forgetting something else, but that's all I remember.
  • Mark O'Connor
    Mark O'Connor almost 14 years
    You mentioned in your post above that the extra jar was introduced recently? That extra jar needs to be included in the manifest "CLass-Path" entry for the CC.jar. I'll update my posting
  • Justian Meyer
    Justian Meyer almost 14 years
    I'll take a look at this after lunch. Thanks so much!
  • Justian Meyer
    Justian Meyer almost 14 years
    Never mind. I made a mistake. My main method is now in ExcelFile and I changed that in build.xml, but I still get an error. New Error: "Exception in thread "main" java.lang.NoSuchMethodError: main". Main definitely exists. I'm uploading my main class right now (this could be the issue- the construction is... odd).
  • Justian Meyer
    Justian Meyer almost 14 years
    I switched back to my old build.xml and I got the same new error =/
  • hfontanez
    hfontanez almost 7 years
    This solution might not work if the classpath need to enforce a certain order in class loading (because of the use in the '*' wildcard. Am I correct in my assumption?
  • Mark O'Connor
    Mark O'Connor almost 7 years
    @hfontanez Indeed, but only an issue if two jars contain two different versions of the same class. Something I would not recommend
  • hfontanez
    hfontanez almost 7 years
    @MarkO'Connor agreed 100%. The problem is that one could make the mistake in not identifying library dependencies properly and you could end up with two libraries using different versions of the same third-party libraries. I have been guilty of that sin myself. I thought it was prudent to mention this fact so that others don't end up following this suggestion blindly.
  • Mark O'Connor
    Mark O'Connor almost 7 years
    @hfontanez Point taken. I generally avoid this problem by using Apache ivy to manage my 3rd party dependencies.