tomcat

Configuring Jersey Servlet Container on Embedded Tomcat

One of the ways of configuring a Jersey Servlet Container on Tomcat is via a web.xml file.
For example, typically the following web.xml file is used where a Servlet and its mappings are defined:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>  
<web-app xmlns="http://java.sun.com/xml/ns/javaee"  
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <servlet>
        <servlet-name>API</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>

        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.app.v2.RestApplication</param-value>
        </init-param>

    </servlet>
    <servlet-mapping>
        <servlet-name>API</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
</web-app>  

When starting an embedded version of Tomcat the same Jersey Servlet that was defined in the XML file can be defined via code:

    RestApplication restApplication = new RestApplication();
    ServletContainer servletContainer = new ServletContainer(restApplication);
    Tomcat.addServlet(ctx, "api", servletContainer);
    ctx.addServletMapping("/api/*", "hubble-api");

There are three steps involved in configuring Jersey:

Tomcat Embedded Server Configuration

Tomcat initially started as a web server container, meaning that one Tomcat server could host several web applications. With the popularity of languages such as Ruby and Node growing and frameworks like rails and express becoming very popular for web applications the new pattern of embedding the web server as part of the application itself removing the need of any external dependencies became very common.
Even though this feature is not very well documented, Tomcat does support an embedded configuration.

Below is an example of converting a standard server.xml to an embedded server.

<?xml version='1.0' encoding='utf-8'?>

<Server port="8010" shutdown="SHUTDOWN">  
  <!--APR library loader. Documentation at /docs/apr.html -->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

    <Service name="Catalina">

        <Connector port="8080"
                   protocol="org.apache.coyote.http11.Http11NioProtocol"
                   compression="on"
                   compressableMimeType="application/json"
                   connectionTimeout="5000"
                   maxThreads="50"
                   minSpareThreads="50"
                   redirectPort="9410" />

        <Engine name="Catalina" defaultHost="localhost">

            <Host name="localhost"  appBase="webapps"
                  unpackWARs="true" autoDeploy="false">

            </Host>
        </Engine>
    </Service>

</Server>  

The main components defined in the XML file are:

  • Server
  • Listeners
  • Service
  • Connector
  • Engine
  • Host

When configuring an embedded Tomcat, the xml file listed above can be converted to java code as follows:

Tomcat tomcat = new Tomcat();  
Path tempPath = Files.createTempDirectory("tomcat-base-dir");  
tomcat.setBaseDir(tempPath.toString());

// Configure connector
Connector connector = new Connector();  
connector.setPort(8080);  
connector.setProtocol(Http11NioProtocol.class.getName());  
connector.setProperty("maxThreads", "100");  
connector.setProperty("minSpareThreads", "100");  
connector.setProperty("compression", "on");  
connector.setProperty("compressableMimeType", "application/json");  
connector.setProperty("connectionTimeout", "8000");  
tomcat.getService().addConnector(connector);  
tomcat.setConnector(connector);

// Configure tomcat life cycle listeners
JreMemoryLeakPreventionListener jreMemoryLeakPreventionListener = new JreMemoryLeakPreventionListener();  
GlobalResourcesLifecycleListener globalResourcesLifecycleListener = new GlobalResourcesLifecycleListener();  
ThreadLocalLeakPreventionListener threadLocalLeakPreventionListener = new ThreadLocalLeakPreventionListener();  
VersionLoggerListener versionLoggerListener = new VersionLoggerListener();  
tomcat.getServer().addLifecycleListener(jreMemoryLeakPreventionListener);  
tomcat.getServer().addLifecycleListener(globalResourcesLifecycleListener);  
tomcat.getServer().addLifecycleListener(threadLocalLeakPreventionListener);  
tomcat.getServer().addLifecycleListener(versionLoggerListener);

// Create web context
File webContentFolder = Files.createTempDirectory("default-doc-base").toFile();  
StandardContext ctx = (StandardContext) tomcat.addWebapp("", webContentFolder.getAbsolutePath());

// Disable jar scanner for better start up performance
StandardJarScanFilter jarScanFilter = (StandardJarScanFilter) ctx.getJarScanner().getJarScanFilter();  
jarScanFilter.setTldSkip("*");

tomcat.start();  
tomcat.getServer().await();  

*It is important to set both the default Tomcat connector as well as the service connector the application will use. By default, Tomcat configures an HTTP/1.1 connector listening on port 8080, so if only the service connector is set Tomcat will try to load both connectors at once.