如何从命令行运行 WAR 中的类?

我有一个 Java 类,它有一个 main,我过去常常作为一个独立的应用程序从命令行运行,例如。

java -jar myjar.jar params

我需要重新打包代码以便在 apache 下运行,所有的代码,包括旧 jar 中的入口点类,最终都放到了一个 WAR 文件中,以便轻松地部署到 Web 服务器中。

但是,我仍然希望能够从命令行运行它,而且代码没有改变,都在里面,我只是不知道如何让它运行。

我是这么想的。

我以为战争就像一个罐子,所以

java -jar mywar.war params

这个错误说明在清单中没有定义主类。

我手动添加了一个清单到战争,并再次尝试,与同样的效果。

我注意到在我的 war 文件夹中有一个名为 META-INF 的文件夹,其中包含一个 Manif.mf,所以我在那个声明我的主类的文件夹中添加了一行代码,就像我在一个普通清单文件中添加一行代码一样..。

Manifest-Version: 1.0
Main-Class: mypackage.MyEntryPointClass

这给出了一个 noClassDefFoundError mypackage.MyEntryPointClass,这是一种进步。这让我相信这只是一个路径问题,所以我尝试了

Manifest-Version: 1.0
Main-Class: WEB-INF.classes.mypackage.MyEntryPointClass

我现在得到同样的错误,但是通过堆栈跟踪..。

Exception in thread "main" java.lang.NoClassDefFoundError: WEB-INF/classes/mypackage/MyEntryPointClass (wrong name: mypackage/MyEntryPointClass)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$100(Unknown Source)
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)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)

我在谷歌上搜索了一下,但是没有找到任何答案,我在这里读到了一些其他的问题,它们稍微有些不同,所以我想我应该发表一下。

Java 1.5我觉得这没什么区别。

239133 次浏览

The rules of locating classes in an archive file is that the location of the file's package declaration and the location of the file within the archive have to match. Since your class is located in WEB-INF/classes, it thinks the class is not valid to run in the current context.

The only way you can do what you're asking is to repackage the war so the .class file resides in the mypackage directory in the root of the archive rather than the WEB-INF/classes directory. However, if you do that you won't be able to access the file from any of your web classes anymore.

If you want to reuse this class in both the war and outside from the java command line, consider building an executable jar you can run from the command line, then putting that jar in the war file's WEB-INF/lib directory.

A war is a webapp. If you want to have a console/standalone application reusing the same classes as you webapp, consider packaging your shared classes in a jar, which you can put in WEB-INF/lib. Then use that jar from the command line. Thus you get both your console application, and you can use the same classes in your servlets, without making two different packages. This, of course, is true when the war is exploded.

Well, according to Wikipedia, with a WAR file, the classes that get loaded into the classpath are in the "/WEB-INF/classes" and "/WEB-INF/lib" directory.

You could try simply putting a copy of the classes on the root file system of the zip file (which is what a war/jar is). I'm not sure if that would work though.

You can always just create two separate files.

You can do what Hudson (continuous integration project) does. you download a war which can be deployed in tomcat or to execute using

java -jar hudson.war

(Because it has an embedded Jetty engine, running it from command line cause a server to be launched.) Anyway by looking at hudson's manifest I understand that they put a Main class in the root for the archive. In your case your war layout should be look like:

under root:

  • mypackage/MyEntryPointClass.class
  • WEB-INF/lib
  • WEB-INF/classes
  • META-INF/MANIFEST.MF

while the manifest should include the following line:

Main-Class: mypackage.MyEntryPointClass

please notice that the mypackage/MyEntryPointClass.class is accessable from the command line only, and the classes under WEB-INF/classes are accessable from the application server only.

HTH

In Maven project, You can build jar automatically using Maven War plugin by setting archiveClasses to true. Example below.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archiveClasses>true</archiveClasses>
</configuration>
</plugin>

To execute SomeClass.main(String [] args) from a deployed war file do:

  1. Write class SomeClass.java that has a main method method i.e. (public static void main(String[] args) {...})
  2. Deploy your WAR
  3. cd /usr/local/<yourprojectsname>/tomcat/webapps/projectName/WEB-INF
  4. java -cp "lib/jar1.jar:lib/jar2.jar: ... :lib/jarn.jar" com.mypackage.SomeClass arg1 arg2 ... arg3

Note1: to see if the class SomeOtherClass.class is in /usr/tomcat/webapps/<projectName>/WEB-INF/lib run:

cd /usr/tomcat/webapps/projectName/WEB-INF/lib &&
find . -name '*.jar' | while read jarfile; do if jar tf "$jarfile" | grep SomeOtherClass.class; then echo "$jarfile"; fi; done

Note2: Write to standard out so you can see if your main actually works via print statements to the console. This is called a back door.

Note3: The comment above by Bozhidar Bozhanov seems correct

It's not possible to run a java class from a WAR file. WAR files have a different structure to Jar files.

To find the related java classes, export (preferred way to use ant) them as Jar put it in your web app lib.

Then you can use the jar file as normal to run java program. The same jar was also referred in web app (if you put this jar in web app lib)

Similar to what Richard Detsch but with a bit easier to follow (works with packages as well)

Step 1: Unwrap the War file.

jar -xvf MyWar.war

Step 2: move into the directory

cd WEB-INF

Step 3: Run your main with all dependendecies

java -classpath "lib/*:classes/." my.packages.destination.FileToRun

If you're using Maven, just follow the maven-war-plugin documentation about "How do I create a JAR containing the classes in my webapp?": add <attachClasses>true</attachClasses> to the <configuration> of the plugin:

<project>
...
<artifactId>mywebapp</artifactId>
<version>1.0-SNAPSHOT</version>
...
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<attachClasses>true</attachClasses>
</configuration>
</plugin>
</plugins>
</build>
...
</project>

The you will have 2 products in the target/ folder:

  • The project.war itself
  • The project-classes.jar which contains all the compiled classes in a jar

Then you will be able to execute a main class using classic method: java -cp target/project-classes.jar 'com.mycompany.MainClass' param1 param2

the best way if you use Spring Boot is :

1/ Create a ServletInitializer extends SpringBootServletInitializer Class . With method configure which run your Application Class

2/ Generate always a maven install WAR file

3/ With this artefact you can even :

    . start application from war file with java -jar file.war


. put your war file in your favorite Web App server (like tomcat, ...)

As an alternative option, include into the war file, the rest service to trigger the application logic via url. Deploy the war file onto any web/application server you want.

Then you can start your application via any command-line based HTTP client like curl on Linux.

The disadvantage: usually these HTTP clients are different on different OSs. Which is not critical for a lot of cases. Also you can install curl on Windows.