Category Archives: J2EE

Self hosting Tomcat in a Java Web application

Following on from my previous post where I created a web application in Java, let’s now look at hosting the WAR within an embedded Tomcat instance.

Adding dependencies to include embedded Tomcat

Add the following to your pom.xml (after the description tag)

<properties>
   <tomcat.version>9.0.0.M6</tomcat.version>
</properties>

Note: There’s a newer version of a couple of the dependencies, but this version exists for all three of the dependencies we’re about to add.

Now, add following dependencies

<dependencies>
   <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-core</artifactId>
      <version>${tomcat.version}</version>
   </dependency>
   <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-jasper</artifactId>
      <version>${tomcat.version}</version>
   </dependency>
   <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-logging-juli</artifactId>
      <version>${tomcat.version}</version>
   </dependency>
</dependencies>

Time to run mvn install if not auto-importing.

Time to create the entry point/application

Let’s add a new package to the src folder, com.putridparrot now add a Java file, mine’s HostApp.java, here’s the code

package com.putridparrot;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;

import javax.servlet.ServletException;
import java.io.File;

public class HostApp {
    public static void main(String[] args) throws ServletException, LifecycleException {

        Tomcat tomcat = new Tomcat();
        tomcat.setBaseDir("temp");
        tomcat.setPort(8080);

        String contextPath = "";
        String webappDir = new File("web").getAbsolutePath();

        tomcat.addWebapp(contextPath, webappDir);

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

In the above we create an instance of Tomcat and then set up the port and the context for the web app, including the path to the web folder where our index.jsp is hosted in our WAR file.

We then start the server and then wait until the application is closed.

By the way, it’s also worth adding logging to the pom.xml. The embedded Tomcat server using “standard” Java based logging, so we can add the following to the pom.xml dependencies

<dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.15</version>
</dependency>

Create a run configuration

Now select Edit Configuration and add a configuration that runs main from HostApp.

Adding gzip/compression support, before we start the server. Hence select Application and set Main class to com.putridparrot.HostApp.

Testing

Run the newly added application configuration, don’t worry about the exceptions. You should see a line similar to

INFO: Starting ProtocolHandler [http-nio-8080]

At this point the server is running, so navigate your browser to http://localhost:8080 and check that the index.jsp page is displayed.

Configuring the embedded Tomcat for gzip/compression support

I’ve been looking into compression with gzip on some web code and hence wanted to configure this embedded Tomcat server to handle compression if/when requested via Accept-Type: gzip etc.

So add the following to the main method (before tomcat.start())

Connector c = tomcat.getConnector();
c.setProperty("compression", "on");
c.setProperty("compressionMinSize", "1024");
c.setProperty("noCompressionUserAgents", "gozilla, traviata");
c.setProperty("compressableMimeType", "text/html,text/xml,text/css,application/json,application/javascript");
tomcat.setConnector(c);

You’ll also need the import import org.apache.catalina.connector.Connector;.

Testing gzip/compression

To test whether Tomcat is using compression is best done with something like curl. I say this because, whilst you can use a browser (such as Chrome’s) debug tools and see a response with Content-Type: gzip, I really wanted to see the raw compressed data to feel I really was getting compressed responses, the browser automatically decompressed the responses for me.

Go the main method and just change “on” to “force”

i.e.

c.setProperty("compression", "force");

This just forces compression to be on all the time.

Now to test our server is set up correctly. Thankfully Windows 10 seems to have curl available from a command prompt, so this works in Linux or Windows.

Run the following

curl -H "Accept-Encoding: gzip,deflate" -I "http://localhost:8080/index.jsp"

This command adds the header Accept-Encoding and then outputs the header (-I) results from accessing the URL. This should show a Content-Encoding: gzip if everything was set up correctly.

To confirm everything is as expected, we can download the content from the URL and save it (in this case saved to index.jsp.gz) and then use gzip -d to decompress the file if we wish.

curl -H "Accept-Encoding: gzip,deflate" "http://localhost:8080/index.jsp" -o index.jsp.gz

This will create index.jsp.gz which should be compressed so we can use

gzip -d index.jsp.gz

to decompress it and we should see the expected web page.

Creating a Java Web Application with IntelliJ

Creating our project

  • Choose File | New | Project
  • Select Java Enterprise
  • Then tick the Web Application

If you have an application server setup, select or add it using New, I’m going to ultimately add an embedded Tomcat container, so leaving this blank.

Finally give the project a name, i.e. MyWebApp.

Adding a Maven pom.xml

I want to use Maven to import packages, so add a pom.xml to the project root add then supply the bare bones (as follows)

<?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>MyWebApp</groupId>
    <artifactId>MyWebApp</artifactId>
    <packaging>war</packaging>
    <name />
    <version>MyWebApp.0.0.1-SNAPSHOT</version>
    <description />

</project>

In IntelliJ, select the pom.xml, right mouse click and select Add as Maven project.

Next, we want to tell Maven how to compile and generate our war, so add the following after the description tag in the pom.xml

<build>
   <sourceDirectory>${basedir}/src</sourceDirectory>
   <outputDirectory>${basedir}/web/WEB-INF/classes</outputDirectory>
   <resources>
      <resource>
         <directory>${basedir}/src</directory>
         <excludes>
            <exclude>**/*.java</exclude>
         </excludes>
      </resource>
   </resources>
   <plugins>
      <plugin>
         <artifactId>maven-war-plugin</artifactId>
         <configuration>
            <webappDirectory>${basedir}/web</webappDirectory>
            <warSourceDirectory>${basedir}/web</warSourceDirectory>
         </configuration>
      </plugin>
      <plugin>
         <artifactId>maven-compiler-plugin</artifactId>
         <configuration>
            <source>1.8</source>
            <target>1.8</target>
         </configuration>
      </plugin>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <configuration>
            <source>8</source>
            <target>8</target>
         </configuration>
      </plugin>
   </plugins>
</build>

Creating a run configuration

Whilst my intention is to add a Tomcat embedded server, we can create a new run configuration at this point to test everything worked.

Select the Run, Edit Configuration option from the toolbar or Run | Edit Configuration and click + and add a Tomcat Server | Local.

Mine’s set with the URL http://localhost:8080/. Give the configuration a name and don’t forget to select the Deployment tab, press the + and then click Aritifact… I selected MyWebApp:war.

Press OK and OK again to finish the configuration and now you can run Tomcat locally and deploy the war.

Don’t forget to execute mvn install to build you war file.