How to embed Tomcat 6?
Asked Answered
O

6

58

I'm currently running my webapps on Tomcat 6 in production, and would like to evaluate running Tomcat in embedded mode.

Is there a good tutorial or other resource besides what's in the api documentation?

Oquinn answered 12/3, 2009 at 19:0 Comment(4)
Any reason to use Tomcat rather than Jetty, which is well documented for this use case and commonly used embedded? If you're simply looking for a Servlet container that you can embed, Jetty does this readily.Weather
Embed Jetty instead. However, there is tomcat-embed.Roundel
Thanks for the pointer. However, this doesn't look like it's complete, actively developed, or maintained. All commits are dated May 11, 2008, and one of the log messages calls it "far from complete".Oquinn
Worth to look in this blog post: Embedding Tomcat 7Fredette
P
40

Code speaks for itself. See the pom.xml snippet and the class to run tomcat.

    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>catalina</artifactId>
        <version>6.0.18</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>coyote</artifactId>
        <version>6.0.18</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>jasper</artifactId>
        <version>6.0.18</version>
        <scope>test</scope>
    </dependency>


public class RunWebApplicationTomcat {

    private String path = null;
    private Embedded container = null;
    private Log logger = LogFactory.getLog(getClass());

    /**
     * The directory to create the Tomcat server configuration under.
     */
    private String catalinaHome = "tomcat";

    /**
     * The port to run the Tomcat server on.
     */
    private int port = 8089;

    /**
     * The classes directory for the web application being run.
     */
    private String classesDir = "target/classes";

    /**
     * The web resources directory for the web application being run.
     */
    private String webappDir = "mywebapp";

    /**
     * Creates a single-webapp configuration to be run in Tomcat on port 8089. If module name does
     * not conform to the 'contextname-webapp' convention, use the two-args constructor.
     * 
     * @param contextName without leading slash, for example, "mywebapp"
     * @throws IOException
     */
    public RunWebApplicationTomcat(String contextName) {
        Assert.isTrue(!contextName.startsWith("/"));
        path = "/" + contextName;
    }

    /**
     * Starts the embedded Tomcat server.
     * 
     * @throws LifecycleException
     * @throws MalformedURLException if the server could not be configured
     * @throws LifecycleException if the server could not be started
     * @throws MalformedURLException
     */
    public void run(int port) throws LifecycleException, MalformedURLException {
        this.port = port;
        // create server
        container = new Embedded();
        container.setCatalinaHome(catalinaHome);
        container.setRealm(new MemoryRealm());

        // create webapp loader
        WebappLoader loader = new WebappLoader(this.getClass().getClassLoader());

        if (classesDir != null) {
            loader.addRepository(new File(classesDir).toURI().toURL().toString());
        }

        // create context
        // TODO: Context rootContext = container.createContext(path, webappDir);
        Context rootContext = container.createContext(path, webappDir);
        rootContext.setLoader(loader);
        rootContext.setReloadable(true);

        // create host
        // String appBase = new File(catalinaHome, "webapps").getAbsolutePath();
        Host localHost = container.createHost("localHost", new File("target").getAbsolutePath());
        localHost.addChild(rootContext);

        // create engine
        Engine engine = container.createEngine();
        engine.setName("localEngine");
        engine.addChild(localHost);
        engine.setDefaultHost(localHost.getName());
        container.addEngine(engine);

        // create http connector
        Connector httpConnector = container.createConnector((InetAddress) null, port, false);
        container.addConnector(httpConnector);

        container.setAwait(true);

        // start server
        container.start();

        // add shutdown hook to stop server
        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                stopContainer();
            }
        });
    }
    /**
     * Stops the embedded Tomcat server.
     */
    public void stopContainer() {
        try {
            if (container != null) {
                container.stop();
            }
        } catch (LifecycleException exception) {
            logger.warn("Cannot Stop Tomcat" + exception.getMessage());
        }
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public static void main(String[] args) throws Exception {
        RunWebApplicationTomcat inst = new RunWebApplicationTomcat("mywebapp");
        inst.run(8089);
    }

    public int getPort() {
        return port;
    }

}
Pilgarlic answered 13/3, 2009 at 0:31 Comment(2)
I'm not able to get this running, only getting 404s.. Am I missing something?Crosseyed
Did you change the attributes catalinaHome, port, classesDir, webappDir accordingly?Pilgarlic
E
16

Though this post is some what aged, I m answering my own answer as it could save some other' time

package com.creativefella;

import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Embedded;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TomcatServer {
    private Embedded server;
    private int port;
    private boolean isRunning;

    private static final Logger LOG = LoggerFactory.getLogger(TomcatServer.class);
    private static final boolean isInfo = LOG.isInfoEnabled();


/**
 * Create a new Tomcat embedded server instance. Setup looks like:
 * <pre><Server>
 *    <Service>
 *        <Connector />
 *        <Engine&gt
 *            <Host>
 *                <Context />
 *            </Host>
 *        </Engine>
 *    </Service>
 *</Server></pre>
 * <Server> & <Service> will be created automcatically. We need to hook the remaining to an {@link Embedded} instnace
 * @param contextPath Context path for the application
 * @param port Port number to be used for the embedded Tomcat server
 * @param appBase Path to the Application files (for Maven based web apps, in general: <code>/src/main/</code>)
 * @param shutdownHook If true, registers a server' shutdown hook with JVM. This is useful to shutdown the server
 *                      in erroneous cases.
 * @throws Exception
 */
    public TomcatServer(String contextPath, int port, String appBase, boolean shutdownHook) {
        if(contextPath == null || appBase == null || appBase.length() == 0) {
            throw new IllegalArgumentException("Context path or appbase should not be null");
        }
        if(!contextPath.startsWith("/")) {
            contextPath = "/" + contextPath;
        }

        this.port = port;

        server  = new Embedded();
        server.setName("TomcatEmbeddedServer");

        Host localHost = server.createHost("localhost", appBase);
        localHost.setAutoDeploy(false);

        StandardContext rootContext = (StandardContext) server.createContext(contextPath, "webapp");
        rootContext.setDefaultWebXml("web.xml");
        localHost.addChild(rootContext);

        Engine engine = server.createEngine();
        engine.setDefaultHost(localHost.getName());
        engine.setName("TomcatEngine");
        engine.addChild(localHost);

        server.addEngine(engine);

        Connector connector = server.createConnector(localHost.getName(), port, false);
        server.addConnector(connector);

        // register shutdown hook
        if(shutdownHook) {
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    if(isRunning) {
                        if(isInfo) LOG.info("Stopping the Tomcat server, through shutdown hook");
                        try {
                            if (server != null) {
                                server.stop();
                            }
                        } catch (LifecycleException e) {
                            LOG.error("Error while stopping the Tomcat server, through shutdown hook", e);
                        }
                    }
                }
            });
        }

    }

    /**
     * Start the tomcat embedded server
     */
    public void start() throws LifecycleException {
        if(isRunning) {
            LOG.warn("Tomcat server is already running @ port={}; ignoring the start", port);
            return;
        }

        if(isInfo) LOG.info("Starting the Tomcat server @ port={}", port);

        server.setAwait(true);
        server.start();
        isRunning = true;
    }

    /**
     * Stop the tomcat embedded server
     */
    public void stop() throws LifecycleException {
        if(!isRunning) {
            LOG.warn("Tomcat server is not running @ port={}", port);
            return;
        }

        if(isInfo) LOG.info("Stopping the Tomcat server");

        server.stop();
        isRunning = false;
    }

    public boolean isRunning() {
        return isRunning;
    }

}

I also faced the 404 error and struggled some time. By seeing the log 'INFO: No default web.xml', I suspected it (if that is a warning, would've been easy to spot). The trick being using the web.xml ( rootContext.setDefaultWebXml("web.xml") ) supplied with Tomcat (conf/web.xml). The reason being, it includes the DefaultServlet, which serves the static files likes HTML, JS. Either use the web.xml or register the servlet manually in your code.

Usage:

// start the server at http://localhost:8080/myapp
TomcatServer server = new TomcatServer("myapp", 8080, "/src/main/", true);
server.start();
// .....
server.stop();

Do not forget to place the default web.xml in the same directory of this program or point to the correct location.

It should be noted that the shutdown hook is inspired from Antonio's answer.

Eudocia answered 26/9, 2011 at 12:34 Comment(2)
I tried your example, my application goes up, I see spring being initalized but when I try to access my application, I get spring error messages saying that no mappings were found for my jsps.Decuple
A minor update: The org.apache.catalina.startup.Embedded is deprecated and is advised to use org.apache.catalina.startup.Tomcat instead. Making this change many chain changes will follow to make the code runnable.Tsang
H
12

There are a number of reasons why one might use Tomcat over Jetty:

  1. One is already familiar with Tomcat
  2. One is developing web applications that need to be easily transported to a Tomcat installation
  3. The Jetty developer documentation is actually spottier than Tomcat's (amazing!)
  4. Getting questions answered in the Jetty community can sometimes take years, as in 2007. see Embedding Jetty
  5. Important: After Jetty 6.1.*, each web application opens into its own JVM, so if you're trying to gain programmatic access between your standalone access and your web app, your only hope is via a web API.
  6. If it's an issue for you, Tomcat is an open source project who intellectual property is owned by the Apache Foundation, Jetty is open source but owned by a small private company (Mortbay Consulting)

Point #5 has been important in my work. For example, I can gain direct access to a JSPWiki instance via Tomcat, but it's completely inaccessible when using Jetty. I asked for a solution to that in 2007 and haven't yet heard an answer. So I finally gave up and began using Tomcat 6. I've looked into Glassfish and Grizzly, but so far Tomcat is (amazingly) the most stable and well-documented web container (which isn't saying much really).

Heterosis answered 14/12, 2009 at 4:1 Comment(4)
"each web application opens into its own JVM": where did you get this information? I doubt this is accurate.Zambia
Sounds more like a classloader thing. Hard to tell.Deflective
I'm pretty certain webapps do not open in their own JVM. They use separate classloaders by default. To get the classloader behaviour you want just use webapp.setParentLoaderPriority(true); - I think this is in the Jetty documentation.Lamdin
I've used Tomcat, Jetty, and Grizzly all in production and pretty much agree with all that was said in this answerOffwhite
G
2

This might help.

If you download the source package for Tomcat6.x, you get this class:

http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/startup/Catalina.html#main(java.lang.String[])

Which is an example of how to use the Embedd class: its a shell to stop|start a specific Tomcat installation. (I mean you can set up CATALINA_BASE to point at an existing Tomcat installation).

If you compile this you can run like this:

java -D"catalina.base=%CATALINA_BASE%" -D"catalina.home=%CATALINA_HOME%" org.apache.catalina.startup.Catalina start

I'm not sure how to alter this code to shutdown the server yet though!

Gastrotomy answered 3/7, 2009 at 16:7 Comment(0)
T
0

After reading this thread some months ago, I wrote this project: spring-embedded-tomcat. It can be used to embed tomcat6 into Spring-based applications.

Trilinear answered 16/3, 2012 at 15:44 Comment(0)
C
0

I think with Tomcat 7 or Jetty 9 embedding is easier. Here you will find a nice introduction: http://www.hascode.com/2013/07/embedding-jetty-or-tomcat-in-your-java-application/

Complacence answered 11/7, 2013 at 8:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.