WAR-less Java Web Apps

Have you ever thought about why in Java we package up web apps into WAR files (or WAR directory structures)? It certainly is a convenient way to move an application and its dependencies from one place to another. But wouldn’t it be nice if everything could just stay in its original location and there wouldn’t be any moving of files around? Wouldn’t it also be nice if you specified your required version of Jetty or Tomcat just like you do with every other dependency? The WAR-less approach is one that is catching on as emerging Java web frameworks like Play! ditch the WAR files. With standard Java web apps we can also ditch the WAR files by simply launching an embedded Jetty or Tomcat server. Let’s give this a try and see how it goes.

For this experiment I’m going to use Maven and Jetty. This will still use the same standard source structure for a WAR file (src/main/java, src/main/webapp, etc). The major difference is that I will actually startup Jetty using a good-old static void main. This is similar to using the jetty:run goal but will allow us to have the same exact setup in development and in production. The static stuff will be in src/main/webapp, the compiled classes will be in target/classes, and the dependencies will be right were Maven downloaded them to. First, here is a little Java class (src/main/java/foo/Main.java) that sets up a Jetty server and starts it:

package foo;
import java.io.File;
import java.net.URL;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.*;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.webapp.WebAppContext;
public class Main
  public static void main(String[] args) throws Exception
    String webappDirLocation = "src/main/webapp/";
    Server server = new Server(8080);
    WebAppContext root = new WebAppContext();
    root.setDescriptor(webappDirLocation + "/WEB-INF/web.xml");

As you can see, Main just references the webapp directory so I don’t have to copy the stuff from there to another place. Next I have a little test servlet (src/main/java/foo/HelloServlet.java):

package foo;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class HelloServlet extends HttpServlet
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    PrintWriter out = resp.getWriter();
    out.println("hello, world");

And now the web.xml file (src/main/webapp/WEB-INF/web.xml):

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

And finally a pom.xml file that specifies Jetty as a dependency and provides an easy way to run the Main class:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

And now simply run:

mvn compile exec:java

Maven compiles my Java classes into target/classes and then the exec:java goal runs the Main which finds the other WAR assets in the src/main/webapp directory. If you have been following along, make a request to http://localhost:8080/ to verify that it works (which it should).

There are two alternatives to running Jetty from Maven. You can use the Maven appassembler plugin to create start scripts containing the correct CLASSPATH references and then launch Main class using the generated scripts. Or you can use the Maven assembly or shade plugin to create a JAR containing the application and all of its dependencies.

Here is an example section of a pom.xml file for using the appassembler plugin:


To generate the start scripts simply run:

mvn install

Then to run the script set the REPO environment variable to your Maven repository:

export REPO=~/.m2/repository

And then simply run the script:

sh target/bin/main

All of the code for this example is on github.com:

To make all of this even easier, Jetty has a Maven archetype for generating everything for you. To create a new project containing this setup run:

mvn archetype:generate -DarchetypeGroupId=org.mortbay.jetty.archetype -DarchetypeArtifactId=jetty-archetype-assembler -DarchetypeVersion=8.1.9.v20130131

And now you are ready to build a WAR-less Java web app!

This setup is really the bare minimum required to handle web resource and servlet requests you will need to do a little more work if you want to add JSP support. Find out more about this in the Jetty documentation.

So… What do you think about Java Web apps without WAR files & WAR packaging? I’d love to hear your thoughts!

  • Pingback: http://links.soudev.com.br()

  • Seth

    I’ve often wondered what inspired the Java packaging schemes. Look at the web content model… Create a zip… overwrite content, automatic pickup of the change. If you are asking the question “war what it it really good for?” go one step further and ask the question… wtf I have to restart every time I make a change? I know… I know… VMs these days don’t need to… but does anyone actually trust them to do hot deploys?

    Even better create a NAS, deploy this imaginary new java as “content” model to shared location and then point your Jetty instances to that…. and then load balance an Apache instance across as many “servlet” containers as you heart desired. You could easily create a model where one Apache instance was hitting 20 or 30 jetty instances with hardly any work. Then work up to the multi-site model and there you have some scale…

    Try that model in those were proponents of J2EE… Sun, IBM, Oracle… feels like the packaging schemes were created to sell software and weren’t about getting the job done.

  • Nice tips. This reminds me of how Grails works using grails run-app, or Spring Roo scaffolded apps using mvn tomcat:run. These are also ways to develop WAR-less.

    But at the end of day, if you need to deploy, you need to generate the WAR…

    … or maybe not. How cool would it be if we could instead upload a configuration file to an app server that will tell it where to get the Production code on the cloud. Now that would be really cool.

  • Bill Robertson

    Neat idea, you see this in other languages/libraries. Http server is so ubiquitous, that its just a bit more code that you mix in to your app. I can see it for dev testing. Its very useful for that.

    Trying to come up with possible drawbacks…

    You’re relying on Maven to start the app? I’ll pass on that. If you’re audited (e.g. SOX compliance, lawsuit, “partners” (i.e. people looking to blame you for their mistakes), ugh whatever), could you prove what version of the code you were/are running at a given time? (not that most organizations could do that today with their current process).

    Also, what do you do if you if you’ve got multiple web apps to run on the same machine?

  • Pingback: Embedding Jetty with Netbeans 7 (with Maven) | Giant Flying Saucer()

  • Hi James – We have been using this approach for some time and are using it for applications that need to be run for small enterprises. We have found it very useful and convenient.

    However, thanks for the integration with Maven, we are going to shoot for maven integration next month and your post will certainly help us.

  • Pingback: Containerless Spring MVC()

  • Pingback: Maven + Jetty = Quick WebService « Dev-Smart()

  • Melissa M

    How about if you want to be able to run the war using java -jar myWebApp.war i.e. not using Maven? It seems like it’s a pretty complex task to get jetty to recognize all the libraries needed!

  • Jason Young

    The mvn archetype:generate command above is broken:

    [ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2.2:generate (default-cli) on project standalone-pom: The desired archetype does not exist (org.mortbay.jetty.archetype:jetty-archetype-assembler:7.5.0-SNAPSHOT) -> [Help 1]

    • Try:
      mvn archetype:generate -DarchetypeGroupId=org.mortbay.jetty.archetype -DarchetypeArtifactId=jetty-archetype-assembler -DarchetypeVersion=8.1.9.v20130131

  • Pingback: WAR files vs. Java apps with embedded servers » StevePerkins.net()

  • Pingback: Should An Embedded Jetty Server Be A Spring Bean | Topics in Software Engineering()

  • Nisar

    one question: whats the difference between mvn jetty:run and mvn exec:java

    • The jetty:run approach runs Jetty with a WAR project. The evec:java can run any Java app.

  • Srikanth

    One basic Question: Development seems to be very straight forward. But in containerless/war less approach, how to deploy our containerless webapp in production? how about scalability? availability and load balancing? Could you please shed some info on these topics also please?

    • You app is just a bunch of Jars when you go containerless. So use whatever process you want to setup production deployments. One option is uber jars. For scalability & availability, create multiple instances and put them behind a load balancer.

    • awebster

      I tried deploying to my server and simply running via a scripted call to java -cp “blah.jar:api/*” com.jamesward.Main but whereas running Main from my IDE works fine during dev, this doesn’t work in production. I believe it can’t find the webapp directory. Any ideas? Thanks

      • Does that work locally? What OS? There might be issues with the * expansion on Windows.

        • awebster

          Dev on MacOSX. I got it working by adding src/main to plugin>resources but now I’m seeing duplicates in the jar directory structure (I have a ‘webapp’ with ‘images’ and ‘WEB-INF’ and the packaged JAR has separate webapp, images and WEB-INF folders in the root)…

          • Hmmm… It looks like I never tested the packaging setup and the `src/main/webapp` is hardcoded and relative to the current dir. So this isn’t a very good approach. I’m sure we could make it work but it might be better to go with something more standard, like: http://www.jamesward.com/2012/02/15/webapp-runner-apache-tomcat-as-a-dependency

            If that doesn’t sound like a good option then checkout the Jetty Maven Archetype mentioned in the post. I’d guess that they dealt with this.

  • lakshmanspartan

    how could this be done using Gradle instead of Maven?

  • Agus Muñoz

    Hello James, I’ve created a Maven archetype for setting up projects that uses embeded servers, and a plugin for packaging the application in tar or zip formats without Maven assemblies. I’d be grateful if you give me your opinion: https://github.com/easypack/easypack-archetype and https://github.com/easypack/easypack-maven-plugin . Thanks

    • Cool! This looks great! Thanks for posting it here.

      • Agus Muñoz

        Thanks! Looking forward to get more feedback from more people.