Webapp Runner – Apache Tomcat as a Dependency

John Simone, a fellow co-worker at Heroku, has created webapp-runner which provides an easy way to specify Tomcat as a dependency of your app and launch Tomcat. This is useful for making it simple to test your app locally but it also helps to avoid issues stemming from differences in runtime environments.

Here is how to use it from a Maven pom.xml build:

<?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>org.example</groupId>
    <artifactId>hellojavawebapprunner</artifactId>
    <name>hellojavawebapprunner</name>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
 
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.github.jsimone</groupId>
            <artifactId>webapp-runner</artifactId>
            <version>7.0.22</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <webappDirectory>${project.build.directory}/${project.artifactId}</webappDirectory>
                    <warName>${project.artifactId}</warName>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals><goal>copy-dependencies</goal></goals>
                        <configuration>
                            <includeArtifactIds>webapp-runner</includeArtifactIds>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Then just run the Maven build:

mvn package

And start up Tomcat using webapp-runner pointing to the exploded war:

java -jar target/dependency/webapp-runner-7.0.22.jar target/hellojavawebapprunner

Or you can point it to a war file:

java -jar target/dependency/webapp-runner-7.0.22.jar target/hellojavawebapprunner.war

Since this method still uses war packaging you can do the usual web app deployment in Eclipse or IntelliJ. But that means deploying to a different runtime than what is specified as a dependency. Since one goal of using the webapp-runner is runtime consistency it is better to launch the webapp-runner for local testing. To do that just use the “webapp.runner.launch.Main” class and give it “src/main/webapp” as an argument. Eclipse will include the webapp-runner jar in the launch classpath even though the scope is “provided”. In IntelliJ you need to either manually add the libraries to the runtime classpath or change the scope to “compile” (the default). But this has a side effect of copying the jars into the WEB-INF/lib directory. Maybe someone else knows of a better solution.

I’ve posted a simple example project on GitHub that uses the webapp-runner. Also checkout the webapp-runner project on GitHub for more details on how to use it. If you’d like to try this out on Heroku then you can easily deploy a copy using the java.herokuapp.com app that I created.

Let me know if you have any questions or comments. Thanks!

This entry was posted in Java, Tomcat. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.
  • http://samuelsharaf.wordpress.com/ Samuel Sharaf

    Great Post James! and yes, John continues to rock

  • Adela Kuzniarska

    Hello, my name is Adela and I’m an editor in Flash and Flex Developer’s Magazine. I would like to propose you to write an article about Flex Builder integration with FLASH. For details please write me on adela.kuzniarska@software:disqus .com.pl   
    Regards

  • Arvind S

    Thanks for this post. Can you provide bit more detail information about how to run the application from eclipse without running from command line. Usually will run tomcat inside eclipse which helps to easily link the console error stack to the code and few other advantages.

  • Pavan

    Thanks for the post. I am using webapp-runner to specify Tomcat as a dependency for my app. I want to integrate newrelic with my application. According to Newrelic, I have to add

    SET JAVA_OPTS=%JAVA_OPTS% -javaagent:/full/path/to/newrelic.jar in catalina.bat. How can i modify the tomcat files when i am using webapp-runner to specify Tomcat as a dependency and launch Tomcat.

    • http://www.jamesward.com James Ward

      You can pass this to the java command, like:
      java -javaagent:/full/path/to/newrelic.jar -jar target/dependency/webapp-runner-7.0.22.jar target/hellojavawebapprunner.war

      • Pavan

        Thanks a ton James. It works perfectly. Thanks again for the quick response too.

  • boomkap

    This works great. Only issue I have seen using this deployment method is Tomcat does not respond to SIGTERM that is sent by Heroku and any registered ServletContextListener’s contextDestroyed(ServletContextEvent ctx) method is never called.

  • John Simone

    boomkap: you’re right. That’s fixed in the latest webapp-runner version: https://github.com/jsimone/webapp-runner/issues/22

  • yhrchan

    hi i got a java web app running on heroku with your webapp-runner along with the embedded Tomcat, I was wondering if there would be anyways to use Tomcat’s realm for user authentication and how would I go about setting it up during the startup of Tomcat with the webapp-runner?

    • http://www.jamesward.com James Ward

      I don’t think this is any different with webapp-runner. You can specify your own context config to webapp-runner if that helps.

  • difalco

    Does this have advantages over the approach described here? http://www.javacodegeeks.com/2012/11/standalone-web-application-with-executable-tomcat.html

    • http://www.jamesward.com James Ward

      They are similar approaches. So you can use whichever one works best for you.

  • http://www.facebook.com/profile.php?id=1251418828 Matías Ocampo

    Hi James

    I copy and paste your pom.xml build file and I got following error:

    Failed to execute goal org.apache.maven.plugins:maven-dependency-plugin:
    maven-dependency-plugin (goals “copy-dependencies”, “unpack”) is not supported by m2e. pom.xml /kbappsdev2 line 95 Maven Project Build Lifecycle Mapping Problem

    In line 95 I have this

    // line 95
    package
    copy-dependencies

    webapp-runner

    could you help me? I want to test my java app locally before deploy to heroku.
    Thanks

    • http://www.jamesward.com James Ward

      This might be a Maven version mismatch. I believe I used 3.0.4.

      • http://www.facebook.com/profile.php?id=1251418828 Matías Ocampo

        Thanks for your response James.

        You are right, it’s due to a maven version mismatch.

        I’m using RabbitMQ which is causing some errors to make it run successfully in a local environment, I will have to figure out this which of course is not part of this post.

        Thanks !

  • andrecardoso

    You can run on IDEA by removing the scope=”PROVIDED” from the library entry in the .iml file of your project.

  • ekremk

    I always get this error:

    Adding Context for target/webserviceHeroku_tc3-1.0-SNAPSHOT.war
    Haz 04, 2013 4:15:24 PM org.apache.coyote.AbstractProtocol init
    INFO: Initializing ProtocolHandler ["http-nio-8080"]
    Haz 04, 2013 4:15:24 PM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
    INFO: Using a shared selector for servlet write/read
    Haz 04, 2013 4:15:24 PM org.apache.catalina.core.StandardService startInternal
    INFO: Starting service Tomcat
    Haz 04, 2013 4:15:24 PM org.apache.catalina.core.StandardEngine startInternal
    INFO: Starting Servlet Engine: Apache Tomcat/7.0.34
    Haz 04, 2013 4:15:24 PM org.apache.catalina.startup.ContextConfig getDefaultWebXmlFragment
    INFO: No global web.xml found
    Haz 04, 2013 4:15:26 PM org.apache.catalina.core.StandardContext listenerStart
    SEVERE: Error configuring application listener of class com.sun.xml.ws.transport.http.servlet.WSServletContextListener
    java.lang.ClassNotFoundException: com.sun.xml.ws.transport.http.servlet.WSServletContextListener
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
    at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:532)
    at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:514)
    at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:133)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4727)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5285)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:722)

    Haz 04, 2013 4:15:26 PM org.apache.catalina.core.StandardContext listenerStart
    SEVERE: Skipped installing application listeners due to previous error(s)
    Haz 04, 2013 4:15:26 PM org.apache.catalina.core.StandardContext startInternal
    SEVERE: Error listenerStart
    Haz 04, 2013 4:15:26 PM org.apache.catalina.core.StandardContext startInternal
    SEVERE: Context [] startup failed due to previous errors
    SEVERE: Context [] failed in [org.apache.catalina.core.StandardContext] lifecycle. Allowing Tomcat to shutdown.
    Haz 04, 2013 4:15:26 PM org.apache.coyote.AbstractProtocol start
    INFO: Starting ProtocolHandler ["http-nio-8080"]

    • http://www.jamesward.com James Ward

      Looks like you are missing a dependency somehow.

  • Philippe Riand

    James, I haven’t found an answer to my question (http://stackoverflow.com/questions/20751761/create-tomcat-users-with-heroku). Maybe you can give me a hint?

    My problem is fairly simple: I’m deploying a WAR to Heroku containing my app generated using my own tools (not maven). I need to protect the access to the resources using J2EE users/roles in web.xml. In TOMCAT, you can use a realm that defines the users in a single tomcat-users.xml file, which is perfect for development. But I don’t know how to deploy such a file along with the WAR.

    I tried to define the location using “–tomcat-users-location”, but don’t know where to point. I even used the console to manually copy the file as a peer of the WAR, but never got it to work.

    Any pointer would be highly appreciated. Thanks!

  • Pingback: WAR files vs. Java apps with embedded servers | StevePerkins.net

  • Naga Srinivas

    Hi My Name is naga i am trying use the memcacher as my backed session manager

    i found some documentation from heroku docs
    https://devcenter.heroku.com/articles/memcache-http-sessions-java

    so i added the –session-store argument to the web but its failing the compilation with the following error
    “Unexpected Argument: –session-store”

    I Configured my web process as
    web: java $JAVA_OPTS -jar web/target/dependency/webapp-runner.jar –port $PORT –session-store memcache web/target/*.war

    can you please help on in this

    Thanks in advance



  • View James Ward's profile on LinkedIn