How do I run a class in a WAR from the command line?

223,990

Solution 1

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

Solution 2

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

Solution 3

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.

Solution 4

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

Solution 5

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

Share:
223,990

Related videos on Youtube

Simon
Author by

Simon

Updated on July 05, 2022

Comments

  • Simon
    Simon almost 2 years

    I have a Java class which has a main and I used to run as a standalone app from the command line e.g.

    java -jar myjar.jar params
    

    I needed to repackage the code to run under apache and all my code, including the entry point class from the old jar, has ended up in a WAR file for easy deplyment into the web server.

    However, I still want to be able to run it from the command line and the code has not changed and is all in there, I just can't figure out how to get it to run.

    Here's what I tried...

    I presumed the WAR was just like a jar, so

    java -jar mywar.war params
    

    That failed saying there was no main class defined in the manifest.

    I manually added a manifest to the war and tried again, with the same effect.

    I noticed that in my war I had a folder called META-INF containing a manifest.mf, so I added a line to that declaring my main class as I would to a normal manifest...

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

    This gave a noClassDefFoundError mypackage.MyEntryPointClass, which is progress of a sort. That led me to believe that it was just a path issue, so I tried

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

    I now get the same error, but with a stack trace...

    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)
    

    I've googled for a bit but can't find anything which answers my question, and I read a couple of other questions here which are slightly different, so I thought I would post.

    Java 1.5, not that I think that should make any difference.

    • Dan
      Dan over 14 years
      Haven't tried this, but what about adding a 'Class-Path' entry to the manifest?
    • Stefke
      Stefke over 14 years
      Have you any reasons for this? Why won't you try to keep two different assemblies - one for web and one as a standalone application?
    • Murali VP
      Murali VP over 14 years
      Did you try putting a classpath in the manifest.mf which had WEB-INF/classes and leave the Main-Class as mypackage.MyEntryPointClass ?
    • Simon
      Simon over 14 years
      tried the classpath idea, didn't work
    • Simon
      Simon over 14 years
      @Andrew, I could have two different packages, it just seems wasteful when the content of one is a superset of the content of the other
  • Bozho
    Bozho over 14 years
    putting copies in a public location would deffinitely be ugly
  • JBCP
    JBCP over 11 years
    this is technically correct, so I gave it a +1, but I don't think this solution is a good idea. It requires building a classpath manually, and is very error prone.
  • BxlSofty
    BxlSofty about 7 years
    Agreeing with JBCP above. But I'm using Netbeans and in there you can ask for the properties of your Java class, and it has a Runtime Classpath entry that gathers the full absolute classpath (mine is 4000 chars :) ) Quite a nice way to turn some internals of your webapp into scriptable classes.
  • AdamSkywalker
    AdamSkywalker over 6 years
    the answer I was looking for
  • Sridhar Sarnobat
    Sridhar Sarnobat almost 6 years
    Can this be done without unzipping? The environment I run my stuff on is more tightly constrained than this.
  • Baruch Atta
    Baruch Atta almost 6 years
    Not gonna downvote because the answer contains some good info. However, it does not answer the original question "...run as a standalone app from the command line..."
  • Shondeslitch
    Shondeslitch over 5 years
    run maven clean install
  • vikingsteve
    vikingsteve over 5 years
    Sure but thats not always possible. Sometimes you only have a war and would like to run the main class quickly.
  • ksrb
    ksrb over 5 years
    For windows change the : -> ; so the above command becomes java -classpath "lib/*;classes/." my.packages.destination.FileToRun specifically change