James Ward is a Pricipal Developer Evangelist at salesforce.com. Today he focuses on teaching developers about running Java on the Cloud. James frequently presents at conferences around the world such as JavaOne, Devoxx, and many other Java get-togethers. Along with Bruce Eckel, James co-authored First Steps in Flex. He has also published numerous screencasts, blogs, and technical articles. Starting with Pascal and Assembly in the 80′s, James found his passion for writing code. Beginning in the 90′s he began doing web development with HTML, Perl/CGI, then Java. After building a Flex and Java based customer service portal in 2004 for Pillar Data Systems he became a Technical Evangelist for Flex at Adobe. You can find him tweeting as @_JamesWard, answering questions on StackOverflow.com and posting code at github.com/jamesward. James has posted 20 posts at DZone. You can read more from them at their website. View Full User Profile

WAR-less Java Web Apps

08.24.2011
| 10326 views |
  • submit to reddit

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.setContextPath("/");
    root.setDescriptor(webappDirLocation + "/WEB-INF/web.xml");
    root.setResourceBase(webappDirLocation);
 
    root.setParentLoaderPriority(true);
 
    server.setHandler(root);
 
    server.start();
    server.join();
  }
}

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
{   
 
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
  {
    PrintWriter out = resp.getWriter();
    out.println("hello, world");
    out.close();
  }
}

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"
  version="2.5">
  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>foo.HelloServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

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"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.jamesward</groupId>
  <version>1.0-SNAPSHOT</version>
  <name>warless_java_web_app</name>
  <artifactId>warless_java_web_app</artifactId>
  <packaging>jar</packaging>
 
  <properties>
    <jettyVersion>7.3.1.v20110307</jettyVersion>
  </properties>
 
  <dependencies>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-server</artifactId>
      <version>${jettyVersion}</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-webapp</artifactId>
      <version>${jettyVersion}</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-servlet</artifactId>
      <version>${jettyVersion}</version>
    </dependency>
  </dependencies>
 
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.2</version>
        <configuration>
          <mainClass>foo.Main</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

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:

  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>appassembler-maven-plugin</artifactId>
    <version>1.1.1</version>
    <configuration>
      <assembleDirectory>target</assembleDirectory> 
      <generateRepository>false</generateRepository>
      <programs>
        <program>
          <mainClass>foo.Main</mainClass>
          <name>main</name>
        </program>
      </programs>
    </configuration>
    <executions>
      <execution>
        <phase>package</phase>
        <goals>
          <goal>assemble</goal>
        </goals>
      </execution>          
    </executions>
  </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:
https://github.com/jamesward/warless_java_web_apps

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=7.5.0-SNAPSHOT

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!



References
Published at DZone with permission of its author, James Ward. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

David Grant replied on Wed, 2011/08/24 - 9:21am

I'm not quite clear on what problem you're trying to solve here. I can see the benefit for integration testing, but isn't that possible with the Maven Jetty Plugin anyway?

Nicolas Frankel replied on Wed, 2011/08/24 - 10:36am

Likewise, I do not understand your point. It seems there's a misunderstanding between development-time and deployment-time: it's a good thing to be able to run your app at dev time, but nothing beats having a single artifact for deployment.

Darren Salomons replied on Wed, 2011/08/24 - 12:18pm

Are you suggesting that this would how production deployments would be set up, to run a maven task that invokes a java main with embedded Jetty? I only deal with war files when they are handed off to be tested and then deployed so not really sure of the value here.

Mark Unknown replied on Wed, 2011/08/24 - 1:33pm in response to: Nicolas Frankel

I am not saying the above process is right or wrong, but deploying a war can be a PITA if doing it remotely. Being able to deploy the changed artifacts (still packaged, just not a full blown war) would be wonderful.

Kristian Rink replied on Wed, 2011/08/24 - 2:32pm

Though I found this post to be pretty enlightening, IMHO this is not just about "war or not" but rather about running an application inside a container or running a container embedded in an application. It's a matter of use cases I guess. If one single war application is all you need to run, this approach indeed seems something worth thinking twice about, as by then most of the deployment and container management process might be overhead. However things start to get funny as soon as you run multiple war or ear or ejb-jar modules inside the same container. ;)

Ankur Gupta replied on Thu, 2011/08/25 - 1:51am

Agree with Kristian. Likewise I found the article eye opener in the sense that during development stages of a project, front end (webapp) probably can be bundled with embedded container - always deployed with mocks for external interfaces. This will allow quick development.

Astrid Training replied on Thu, 2011/08/25 - 3:40am

A lot of negative comments to something that can be only constructive (or at the very least tangent to someone's interests). At any rate, I think it's very cool that you took the effort to talk about this, and even give out some code examples. I can see how this could make a Java web application as portable as a jar file, and that can be useful. Thanx

John Waterwood replied on Thu, 2011/08/25 - 3:44am

Why stop at including the Servlet container???

This can easily be taken one step further by also including the JDK. And while at it, why not include the OS as well? Just create an image for eg VmWare, Xen, VirtualBox etc.

Thai Dang Vu replied on Thu, 2011/08/25 - 8:17am

Do you have any Tomcat 7 example like this?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.