Не удается найти основной класс в файле, скомпилированном с помощью Ant
Я компилирую и запускаю свою программу в Eclipse, и все работает нормально, но когда я упаковываю ее с помощью Ant и запускаю ее, я получаю эту ошибку:
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.
Обратите внимание, что это ошибка времени выполнения, а не ошибка компилятора с Ant.
В прошлом я создавал этот проект с 0 проблемами, и теперь он неожиданно для меня срабатывает, когда я добавляю второй пакет в мою папку lib?
Вот файл сборки для справки:
<?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>
Заранее спасибо за помощь!
РЕДАКТИРОВАТЬ:
Вот как выглядит конструкция для моего основного класса (это не фактический файл, а то, на чем я основываю свой). Конструкция очень странная для Java-программы и может вызывать у Ant некоторые проблемы. Любые рекомендации о том, как восстановить это? Я получил кучу ошибок при попытке разделить это на несколько частей. Я просто никогда не видел такую конструкцию раньше (да, я понимаю, КАК она работает (и работает при компиляции), но Ant может не понравиться).
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;
}
}
Обратите внимание, что имя класса на самом деле является UserBean, и в нем содержится закрытый класс с именем ReadingObjects, который содержит основной метод.
3 ответа
Похоже, в вашем пути к классам во время выполнения отсутствует jar, содержащий класс org.supercsv.io.ICsvBeanReader.
Суть в том, что вы не можете установить classpath из командной строки при вызове исполняемого файла jar. Вы должны установить его в манифесте следующим образом:
<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>
Такой подход позволит вам запустить банку следующим образом:
java -jar CC.jar
Без дополнительной записи манифеста вы должны запустить банку следующим образом:
java -cp CC.jar:CC-DSTAMPVALUE.jar jab.jm.main.Test
Заметка
Только CC.jar является исполняемым и требует специального манифеста. Использование этого шаблона означает, что будущие дополнительные файлы jar, помещенные в каталог lib, будут автоматически включены в путь к классам во время выполнения. (Полезно для зависимостей с открытым исходным кодом, таких как log4j)
Очевидно, что при запуске CC.jar вы получите похожую ошибку, если jar-файлы отсутствуют:-)
Это может происходить из-за расположения сгенерированных файлов классов. т. е. когда вы создаете сквозное затмение, он генерирует файлы классов в месте, указанном как папка "Вывод" для ex: bin, и во время работы просматривает это место для файлов классов.
Поэтому проверьте, генерирует ли ваш муравей файлы классов в том же месте, что и выходная папка, указанная в конфигурации BuildPath. Если нет, измените расположение выходной папки на место, где ваш муравей генерирует файлы классов.
Вы пытались явно указать classpath при запуске jar, чтобы убедиться, что на нем установлена новая библиотека?
Возможно, библиотека 1 присутствует в пути к классам по умолчанию, поэтому ваш проект работал нормально, пока вы не добавили библиотеку 2, которой не было. При работе в Eclipse среда IDE может автоматически добавлять библиотеку 2 в путь к классам. Вы можете проверить classpath в Run Configuration вашего проекта в Eclipse и убедиться, что вы включаете все, что есть, когда не запускаете через IDE.