Tomcat 8 and Apache 2

In this post you will see how to setup and configure Apache 2 to run as a proxy server for Tomcat 8.

Introduction

First of all this will be very advance tutorial on how to setup 2 servers for serving web applications. If you are just starting with Apache and Tomcat I will advise you to get good with them alone and then come back to this post. In this post I showed you how to setup basic Apache 2 server for serving static content and how to setup an SSL for it. I also told you that it is not restricted just for that, Apache can be also used as a proxy server and that is what we are going to do in this tutorial/post, we are going to setup Apache 2 as a proxy server for Tomcat 8.

Why?

When I just started writing Java based web applications, all of them were deployed on Tomcat server. The process was, write an application, pack it into a WAR file, deploy WAR on Tomcat, allow internet access to Tomcat, done. This is all ok, this is what we learn in school, this is how we all started. But if you need to host more then 1 application on Tomcat, enable security, configure restrictions and maintain SSL, Tomcat configuration become very hard. This is where this idea comes in. Using Apache as a proxy for Tomcat is noting new, you will still have monolith setup that is system dependent and relatively hard to maintain but you will also get more secure setup, more stabile overall system, load balancing, scalability and some space for automation.

Tomcat 8 setup

I will assume that you already have server setup (dedicated or cloud) with basic Apache 2 and Java installed. with this in place we can install Tomcat by running sudo apt install tomcat8. By default, after installation is done, Tomcat will start on port 8080. To check this you can run sudo service --status-all and you will see [ + ] tomcat8. After this we can open browser and type server IP address and add port 8080, this will grant us with default Tomcat web page, as shown in Figure 1.

Figure 1

Figure 1 - Default Tomcat web page.

This means everything is working correctly. Now I will create simple Spring application that will catch all paths and display Hello world!, as shown in the code below.

  @RestController
  public class TestController {

    @RequestMapping(value = "/**", method = RequestMethod.GET)
    public String get() {
      return "Hello world!";
    }
  }

I will export the application to app.war and push it to the server on /var/lib/tomcat8/webapps using putty pscp command. After this, I will restart Tomcat and access the application from the browser, as shown in Figure 2.

Figure 2

Figure 2 - Access to Hello world application running on Tomcat server.

Creating proxy

Now we are at the main part of this post, Apache 2 and Tomcat 8 are running on the same server independently. Here we will allow access to Tomcat only from localhost while internet request will be proxy by Apache. For this example I will use domain/subdomain qwer.branislav.xyz to access the server from the internet, therefore I will setup my DNS and create Virtual Host on Apache that will handle requests from this subdomain, as shown in this post. If I go to mydomain.com I will get simple HTML page served by Apache, as shown in Figure 3.

Figure 3

Figure 3 - Test HTML page on Apache server on mydomain.com.

Good, with virtual host setup done, we can configure it as a proxy. Idea is to get Hello world! page when accessing mydomain.com. For this we will use AJP. First of all we need to configure Tomcat. Go to /var/lib/tomcat8/conf and make a backup of server.xml so you can rollback. Now open server.xml and modify it as shown below. Do not forget to replace:

  <?xml version="1.0" encoding="UTF-8"?>
  <Server port="8005" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

    <GlobalNamingResources>
      <Resource name="UserDatabase" auth="Container"
                type="org.apache.catalina.UserDatabase"
                description="User database that can be updated and saved"
                factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                pathname="conf/tomcat-users.xml" />
    </GlobalNamingResources>
    <Service name="Catalina">
      <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
      <Engine name="Catalina" defaultHost="localhost">
        <Realm className="org.apache.catalina.realm.LockOutRealm">
          <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                resourceName="UserDatabase"/>
        </Realm>
        <Host name="___MYDOMAIN.COM___" autoDeploy="true"
                appBase="webapps" unpackWARs="true">
          <Alias>___MYDOMAIN.COM___</Alias>
          <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                          prefix="localhost_access_log." suffix=".txt"
                          pattern="%h %l %u %t %r %s %b" resolveHosts="false"/>
          <Context path="" docBase="____APP_NAME____" debug="0" reloadable="true"/>
        </Host>
      </Engine>
    </Service>
  </Server>

As you can see, we have disabled access over HTTP and allowed only AJP requests. If you restart Tomcat you would not be able to access the application from the browser since we are now using AJP. If you get any error after making changes and restarting Tomcat, check out log file and try to resolve the issue. With this done, we can move to Apache virtual host setup. First of all we need to install and enable AJP module for Apache.

  sudo apt install libapache2-mod-jk
  sudo a2enmod jk

Now we can go to /etc/libapache2-mod-jk, make a backup of workers.properies and edit it as shown below. Do not forget to replace MYDOMAIN-COM with your domain, in my case this will be qwer-branislav-xyz.

  workers.tomcat_home=/usr/share/tomcat8
  workers.java_home=/usr/lib/jvm/default-java
  ps=/
  worker.list=MYDOMAIN-COM_worker,loadbalancer
  worker.MYDOMAIN-COM_worker.port=8009
  worker.MYDOMAIN-COM_worker.host=127.0.0.1
  worker.MYDOMAIN-COM_worker.type=ajp13
  worker.MYDOMAIN-COM_worker.lbfactor=1
  worker.loadbalancer.type=lb
  worker.loadbalancer.balance_workers=MYDOMAIN-COM_worker

Everything we need to do after this is to enable worker in virtual host as shown. Do not forget to replace:

  <VirtualHost *:80>
    ServerName ____MYDOMAIN.COM____
    ServerAdmin admin@____MYDOMAIN.COM____

    JKMount /* MYDOMAIN-COM_worker
    DocumentRoot /var/lib/tomcat8/webapps/____APP_NAME____

    ErrorLog ${APACHE_LOG_DIR}/____MYDOMAIN.COM____/error.log
    CustomLog ${APACHE_LOG_DIR}/____MYDOMAIN.COM____/access.log combined

    <Directory /var/lib/tomcat8/webapps/____APP_NAME____>
            Order allow,deny
            Allow from all
            Options -Indexes
    </Directory>
  </VirtualHost>

With this we are done with configuration. You can now restart the Apache and everything should work. If you get an error after restarting the Apache, please checkout the log files at /var/log/apache2/* and try to fix it. If you now go to mydomain.com you should get content of the application running on Tomcat, as shown in Figure 4.

Figure 4

Figure 4 - Accessing web application running on Tomcat over proxy virtual host on Apache.

For SSL setup you will follow same configuration as described in this post but you will configure virtual host on port 443 as a proxy, as shown here.

Conclusion

As you can see this configuration is not easy to setup and we barely scratched the surface, but this is very powerful. As you can image, you don’t need to have Apache and Tomcat on the same server/OS. Apache can be installed on one system and Tomcat on another. Also if you are using embedded Tomcat with JAR packaging (WARs are relatively obsolete), you can configure AJP in application.properties which makes everything much easier to setup. Also this is a basic idea for running distributed systems about which we will talk about in the future.

Share on: