52

I'm trying to make an "executable" war file (java -jar myWarFile.war) that will start up a Jetty webserver that hosts the webapp contained in the WAR file I executed.

I found a page that described how to make what I'm looking for:

However, following that advice along with how I think I'm supposed to make an executable jar (war) isn't working.

I have an Ant task creating a WAR file with a manifest that looks like:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.1
Created-By: 1.5.0_18-b02 (Sun Microsystems Inc.)
Main-Class: Start

The contents of the WAR file look like:

> Start.class
> jsp
>   build.jsp 
> META-INF  
>   MANIFEST.MF
> WEB-INF
>   lib
>     jetty-6.1.22.jar
>     jetty-util.6.1.22.jar

When I try to execute the WAR file, the error is:

Exception in thread "main" java.lang.NoClassDefFoundError: org/mortbay/jetty/Handler
Caused by: java.lang.ClassNotFoundException: org.mortbay.jetty.Handler
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
Could not find the main class: Start. Program will exit.

There appears to be two errors here: one where it seems the JAR files can't be found, and one where the Start class can't be found.

To fix the first one, I put the Jetty JAR files in the base of the WAR file and tried again -- same error. I also tried adding the WEB-INF/lib/<specific-JAR-files> to the Class-Path attribute of the manifest. That did not work either.

Does anyone have any insight as to what I'm doing right/wrong and how I can get this executable WAR file up and running?

twilbrand
  • 1,330
  • 5
  • 19
  • 27
  • 4
    Do you *have* to have a .war file? Why not have a .jar file that contains the .war file, the jetty distribution, and a main class that starts jetty programmatically and loads your webapp to a context. – whaley Apr 24 '10 at 11:22

9 Answers9

50

The link you have in your question provides most of what you need. However, there are a few things that need to be done in addition to that.

Any class files that Jetty needs to start up will need to be located at the root of the war file when it's packaged. We can leverage Ant to do that for us before we <war> the file. The war's manifest file will also need a Main-Class attribute to execute the server.

Here's a step-by-step:

Create your Jetty server class:

This is adapted from the link you provided.

package com.mycompany.myapp;

import java.io.File;
import java.net.URL;
import java.security.ProtectionDomain;

import org.mortbay.jetty.Server;
import org.mortbay.jetty.webapp.WebAppContext;

public final class EmbeddedJettyServer
{
    public static void main(String[] args) throws Exception
    {
        int port = Integer.parseInt(System.getProperty("port", "8080"));
        Server server = new Server(port);

        ProtectionDomain domain = EmbeddedJettyServer.class.getProtectionDomain();
        URL location = domain.getCodeSource().getLocation();

        WebAppContext webapp = new WebAppContext();
        webapp.setContextPath("/");
        webapp.setDescriptor(location.toExternalForm() + "/WEB-INF/web.xml");
        webapp.setServer(server);
        webapp.setWar(location.toExternalForm());

        // (Optional) Set the directory the war will extract to.
        // If not set, java.io.tmpdir will be used, which can cause problems
        // if the temp directory gets cleaned periodically.
        // Your build scripts should remove this directory between deployments
        webapp.setTempDirectory(new File("/path/to/webapp-directory"));

        server.setHandler(webapp);
        server.start();
        server.join();
    }
}

To see what all you can configure here, have a look at the Jetty API documentation.

Build the war with Ant:

This uses a staging directory to unpack the necessary class files into the root of the war so they're accessible when the war is executed.

<target name="war" description="--> Creates self-executing war">
  <property name="staging.dir" location="${basedir}/staging"/>
  <property name="webapp.dir" location="${basedir}/src/webapp"/>

  <mkdir dir="${staging.dir}"/>

  <!-- assumes you have all of your war content (excluding classes and libraries) already structured in a directory called src/webapp -->
  <!-- e.g. -->
  <!-- src/webapp/index.html -->
  <!-- src/webapp/WEB-INF/web.xml -->
  <!-- src/webapp/WEB-INF/classes/my.properties -->
  <!-- etc ... -->
  <copy todir="${staging.dir}">
    <fileset dir="${webapp.dir}" includes="**/*"/>
  </copy>

  <unjar dest="${staging.dir}">
    <!-- you'll have to locate these jars or appropriate versions; note that these include JSP support -->
    <!-- you might find some of them in the downloaded Jetty .tgz -->
    <fileset dir="path/to/jetty/jars">
      <include name="ant-1.6.5.jar"/>
      <include name="core-3.1.1.jar"/>
      <include name="jetty-6.1.24.jar"/>
      <include name="jsp-2.1-glassfish-2.1.v20091210.jar"/><!-- your JSP implementation may vary -->
      <include name="jsp-api-2.1-glassfish-2.1.v20091210.jar"/><!-- your JSP implementation may vary -->
      <include name="servlet-api-2.5-20081211.jar"/><!-- your Servlet API implementation may vary -->
    </fileset>
    <patternset><!-- to exclude some of the stuff we don't really need -->
      <exclude name="META-INF/**/*"/>
      <exclude name="images/**/*"/>
      <exclude name=".options"/>
      <exclude name="about.html"/>
      <exclude name="jdtCompilerAdapter.jar"/>
      <exclude name="plugin*"/>
    </patternset>
  </unjar>

  <!-- copy in the class file built from the above EmbeddedJettyServer.java -->
  <copy todir="${staging.dir}">
    <fileset dir="path/to/classes/dir" includes="com/mycompany/myapp/EmbeddedJettyServer.class"/>
  </copy>

  <war destfile="myapp.war" webxml="${webapp.dir}/WEB-INF/web.xml">
    <fileset dir="${staging.dir}" includes="**/*"/>
    <classes dir="path/to/classes/dir"/><!-- your application classes -->
    <lib dir="path/to/lib/dir"/><!-- application dependency jars -->
    <manifest>
      <!-- add the Main-Class attribute that will execute our server class -->
      <attribute name="Main-Class" value="com.mycompany.myapp.EmbeddedJettyServer"/>
    </manifest>
  </war>

  <delete dir="${staging.dir}"/>
</target>

Execute the war:

If everything's set up properly above, you should be able to:

java -jar myapp.war

// or if you want to configure the port (since we are using the System property in the code)

java -Dport=8443 -jar myapp.war
Rob Hruska
  • 118,520
  • 32
  • 167
  • 192
  • Very very minor comment: when excluding folders from the jars (in ), we can use `**/META-INF/**` instead of `META-INF/**/*` to exclude the actual folder as well as the content of the folder. Otherwise the root folder is still included. – joscarsson Oct 26 '12 at 07:54
  • Why are you `unjar`ing all the dependency jars? They get included in the WEB-INF/lib directory – RTF May 01 '14 at 22:51
  • 1
    @RTF - It's been a while, but I believe it was because those classes are dependencies of the executable war itself (i.e. jetty, EmbeddableJettyServer, etc.), and when you execute the war it's not able to load those from an embedded jar (within itself) - they've got to be classes bundled into the war. – Rob Hruska May 02 '14 at 02:21
45

This is an adaptation for Maven of @RobHruska's answer. It just copies the files of the main class and merges the Jetty JAR files into the WAR file, nothing new, just to simplify your life if you are new -like me- to Maven:

<plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
        <execution>
            <id>move-main-class</id>
            <phase>compile</phase>
            <configuration>
                <tasks>
                    <copy todir="${project.build.directory}/${project.build.finalName}">
                        <fileset dir="${project.build.directory}/${project.build.finalName}/WEB-INF/classes/">
                            <include name="main/*.class" />
                        </fileset>
                    </copy>

                    <unjar dest="${project.build.directory}/${project.build.finalName}">
                        <!-- you'll have to locate these jars or appropriate versions; note that these include JSP support -->
                        <!-- you might find some of them in the downloaded Jetty .tgz -->
                        <fileset dir="${project.build.directory}/${project.build.finalName}/WEB-INF/lib/">
                            <include name="ant-1.6.5.jar"/>
                            <!--<include name="core-3.1.1.jar"/>-->
                            <include name="jetty*"/>
                            <include name="servlet-api*"/>
                        </fileset>

                        <patternset><!-- to exclude some of the stuff we don't really need -->
                            <exclude name="META-INF/**/*"/>
                            <exclude name="images/**/*"/>
                            <exclude name=".options"/>
                            <exclude name="about.html"/>
                            <exclude name="jdtCompilerAdapter.jar"/>
                            <exclude name="plugin*"/>
                        </patternset>
                    </unjar>
                </tasks>
            </configuration>
            <goals>
                <goal>run</goal>
            </goals>
        </execution>
    </executions>
</plugin> 
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.2</version>
    <configuration>
        <archiveClasses>true</archiveClasses>
        <archive>
            <manifest>
                <mainClass>main.Main</mainClass> 
            </manifest>
        </archive>
    </configuration>
</plugin>
AhHatem
  • 1,415
  • 3
  • 14
  • 23
  • How to make it execute? I usually build with `mvn clean compile war:war` but the maven-antrun-plugin is never executed. – Ragnar Jan 15 '15 at 08:04
14

We have figured this out by using jetty-console-maven-plugin.

Whenever you run mvn package it creates another war that can be used with java -jar whateverpackage-runnable.war

        <plugin>
            <groupId>org.simplericity.jettyconsole</groupId>
            <artifactId>jetty-console-maven-plugin</artifactId>
            <version>1.45</version>
            <executions>
                <execution>
                    <goals>
                        <goal>createconsole</goal>
                    </goals>
                </execution>
            </executions>

            <configuration>
                <additionalDependencies>
                    <additionalDependency>
                        <artifactId>jetty-console-requestlog-plugin</artifactId>
                    </additionalDependency>
                    <additionalDependency>
                        <artifactId>jetty-console-gzip-plugin</artifactId>
                    </additionalDependency>
                    <additionalDependency>
                        <artifactId>jetty-console-ajp-plugin</artifactId>
                    </additionalDependency>
                    <additionalDependency>
                        <artifactId>jetty-console-startstop-plugin</artifactId>
                    </additionalDependency>
                </additionalDependencies>
            </configuration>
        </plugin>

It also generates the init.d scripts and everything for you!

Rafael Sanches
  • 1,823
  • 21
  • 28
  • 1
    Is there any documentation on how this works? How do you declare the class that tarts up Jetty? And finally, does this require that the jetty jars be at the root of the war? – Jose Martinez Aug 08 '14 at 15:24
  • it does everything for you.. you just run "mvn package" and it generates an war for you. – Rafael Sanches Apr 01 '16 at 19:16
7

Hudson solves this exact problem using the Winstone servlet container, which supports this use case directly. http://winstone.sourceforge.net/#embedding

Perhaps this would work for you?

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347
  • Jetty was ultimately too confusing with zero help online that I could find. Trying to make my war contain winstone was also proving problematic, but once I put my app into the winstone un-jar'd source and then re-jar it up, it goes fine. – twilbrand Mar 18 '10 at 19:39
  • Kohsuke has since gotten tired of maintaining Winstone so he replaced it with a Winstone-compatible wrapper around Jetty. http://jenkins-ci.361315.n4.nabble.com/Winstone-is-now-powered-by-Jetty-td4678478.html – Thorbjørn Ravn Andersen Dec 11 '13 at 09:46
6

Even though this is kind of old another alternative with Jetty 8 is to simply include the Jetty jars as dependencies in your pom and add the following in your pom (versus an ant script that unpackages the war and repackages it):

            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>1.4</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <createDependencyReducedPom>true</createDependencyReducedPom>
                        <transformers>
                            <transformer
                                implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>JettyStandaloneMain</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <!-- The main class needs to be in the root of the war in order to be 
            runnable -->
        <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
                <execution>
                    <id>move-main-class</id>
                    <phase>compile</phase>
                    <configuration>
                        <tasks>
                            <move todir="${project.build.directory}/${project.build.finalName}">
                                <fileset dir="${project.build.directory}/classes/">
                                    <include name="JettyStandaloneMain.class" />
                                </fileset>
                            </move>
                        </tasks>
                    </configuration>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
3urdoch
  • 7,192
  • 8
  • 42
  • 58
Rodney Beede
  • 61
  • 1
  • 1
3

I take it that by "without maven" you want a jar that you can run by itself and not with "mvn jetty:run"--not that you don't want to use maven at all.

It took me way to long to figure this out because I found many options--none of them dirt simple. Eventually I found this maven plugin from simplericity. It works wonderfully.

schmmd
  • 18,650
  • 16
  • 58
  • 102
1

This is my example ANT extract. The idea is to unpackage the Jetty dependencies and then include them locally just like a normal JAR file:

<!-- Hack: Java doesn't support jars within jars/wars -->
<unjar src="${lib.dir}/container/jetty.jar" dest="${build.dir}/unjar"/>
<unjar src="${lib.dir}/container/jetty-util.jar" dest="${build.dir}/unjar"/>
<unjar src="${lib.dir}/container/servlet-api.jar" dest="${build.dir}/unjar"/>
<unjar src="${lib.dir}/container/jsp-api.jar" dest="${build.dir}/unjar"/>

<!-- Build war file as normal, just including the compiled and unjar'ed files -->
<war destfile="${war.file}" webxml="${config.dir}/web.xml">
    <fileset dir="${build.dir}/classes"/>
    <fileset dir="${build.dir}/unjar"/>
    <fileset dir="${resources.dir}" excludes="*.swp"/>
    <lib dir="${lib.dir}/runtime"/>
    <manifest>
        <attribute name="Main-Class" value="Start"/>
    </manifest>
</war>

Note:

The WEB-INF/lib direcory is for the web applications dependencies. In this case we're packaging the WAR file so that it works like the normal Jetty JAR file on startup

Mark O'Connor
  • 76,015
  • 10
  • 139
  • 185
0
  • Putting .jars inside a .war file root does nothing
  • Putting .jars inside WEB-INF/lib doesn't help the JVM find the Jetty files to even begin launching your .war. It's "too late" to put them there.
  • Putting .jars in the manifest Class-Path only works for external .jar files, not those contained in the .jar

So what to do?

  • Use a build script to simply merge all the .jar files you need into the .war file. This takes a little extra work. It's also a bit ugly in that the compiled code is part of the servable files in the .war
  • Add dependent .jars to the JVM's classpath with "java -cp jetty.jar:... ..." Works though this kind of defeats the purpose of one stand-alone .war
Sean Owen
  • 66,182
  • 23
  • 141
  • 173
  • @Sean he is not putting jars inside .war file root he is putting the class file and it is perfectly valid (as his link points to) – Kannan Ekanath Mar 17 '10 at 12:21
  • His original post shows .jar files in the .war, in WEB-INF/lib. Then he mentions putting .jars at the base of the .war. Your follow-up post shows .jars in the lib/ directory, so what do you mean? I could be wrong as I didn't try it myself, but how does the JVM find Jetty classes in this case? The post you mention shows Maven building the dependencies together, and the Maven output I've ever seen that does this just puts .class files together into one .jar/.war, which was my first answer. – Sean Owen Mar 17 '10 at 15:57
0

I have done a similar thing before but are you launchign the app as "java -jar xxx.war" ?. You have only 2 jars and it is not going to be enough I think. Also try using Jetty 7.0.0M1 (which is the latest version). When I added jetty-server and jetty-webapp as two dependencies (they are from org.eclipse.jetty) I get the following jar's in the lib directory. FYI the org.mortbay.jetty.Handler was in the jetty-server*.jar.

  • jetty-continuation-7.0.0.M1.jar
  • jetty-http-7.0.0.M1.jar
  • jetty-io-7.0.0.M1.jar
  • jetty-security-7.0.0.M1.jar
  • jetty-server-7.0.0.M1.jar
  • jetty-servlet-7.0.0.M1.jar
  • jetty-util-7.0.0.M1.jar
  • jetty-webapp-7.0.0.M1.jar
  • jetty-xml-7.0.0.M1.jar
Kannan Ekanath
  • 16,759
  • 22
  • 75
  • 101