Run Apache Tomcat in a Docker container and deploy a servlet.
- Estricatemente speaking Tomcat is not a server but transforms the caught JPS into servlets that can serve the application in question. On the other hand, Apache Tomcat is open source and can be installed on any system running Java. Deploy Apache Tomcat with Docker Compose 1.
- Java Architect Docker, Kafka, Kubernetes, RESTful Web Services, Spring, SpringBoot, Tomcat - San Francisco, CA 21530 Dice San Francisco, CA 9 hours ago Be among the first 25 applicants.
We are going to use one of the Tomcat images on Docker Hub. There are several images using different versions of the JRE. The JRE version should match the version we’ll use to develop the servlet.
Run Apache Tomcat in a Docker container and deploy a servlet. We are going to use one of the Tomcat images on Docker Hub. There are several images using different versions of the JRE. The JRE version should match the version we’ll use to develop the servlet.
Refer to the previous posts about Docker if you need an introduction (introduction, storage).
1. Create the Container
Run the following commands to create a container for Tomcat 9.0.10 using JRE8
We are using a volume to expose configuration and deployment files externally (located at /usr/local/tomcat). Visit localhost:8080 in a browser to access Tomcat’s main page.
2. Configure Tomcat
If we try to access Server Status, Manager App, or Host Manager we get an access denied response. To resolve this, we follow the instructions on the error page (also refer to this page for additional information).
Let’s find out what folder the volume is using with
Access the folder and edit conf/tomcat-users.xml. Add the following lines (there are commented examples already in the file).
We’re defining two roles and creating a user with those two roles (note: the password used in this example is not secure!).
There is one more change needed. By default, Tomcat allows Manager App access only if the browser is on the same machine as the server. To lift this restriction, access the file webapps/manager/META-INF/context.xml in the volume and change the allow string as below.
This gives permission to all IPs. For am actual deployment, set restrictions as needed. Restart the Tomcat container to make the configuration changes effective. Now we can access the Manager App GUI and we can use it to deploy a sample servlet. Enter the credentials defined in tomcat-users.xml.
3. Set up Eclipse
To develop the servlet, we will use Eclipse’s Web Tool Platform. Refer to Vogella’s Java Web Development with Eclipse WTP Tutorial for more details. Eclipse Oxygen.3a was used for this example.
Make sure you have the necessary packages installed as explained in the tutorial (Help -> About Eclipse -> Installation Details). To install new package, go to Help -> Install New Software.
- Eclipse Java EE Developer Tools
- Eclipse Java Web Developer Tools
- Eclipse Web Developer Tools
For this example, we are not going to set up a local server. We will write the servlet and deploy the .war file manually through the Manager App interface.
4. Write the Servlet
Create a Dynamic Web Project
Enter the Project name (HelloWorld). Leave all other settings default. In particular, we will take care of the Target Runtime later. In the last screen, select create web.xml descriptor.
Press Finish and the project is created. The Java Resources -> src folder is empty. Right click on the project and select New -> Servlet. Name the servlet HelloWorldServlet.
Basic servlet code is generated. The servlet references are missing because we did not set a target runtime. We need to get a copy of the servlet-api.jar from our Tomcat server. It is located in the Docker volume, in the lib directory. Then
- Right click on Eclipse project and select Properties. Open Java Build Path.
- Under the Libraries tab, select Add External JARs, and add the servlet-api.jar.
- The library is added to the project under Java Resources -> Libraries -> Referenced Libraries.
The servlet references in the code are now resolved. In the code, customize the URL mapping.
Update the doGet method
Now, we are ready to generate the WAR file to deploy. Right click on the project and select Export -> WAR file. Export to HelloWorld.war.
5. Deploy the Servlet
Go to the Manager App page. Scroll to the section WAR File to deploy, select HelloWorld.war and press Deploy.
The servlet is added to the list of applications.
In a browser, visit localhost:8080/HelloWorld/Hello. We’re done!
Before we start…
Before we start, we have to agree on one thing – Docker is super cool! Wait… I’ve said this before! And although I’ve also said the next things before, it’s important to note that this will not be an introduction to Docker. I believe there are a lot of articles that can explain the “What?”s and “Why?”s surrounding it. But even though some experience with both Docker and Java is expected, it is not required in order to follow through what we will cover.
The target we’ll be aiming for this time is to compile and package a Java application using Maven and serving the same application with Apache Tomcat without having anything but Docker installed. As we did in Dockerise your PHP application with Nginx and PHP7-FPM, we will use only official Docker Hub repositories. I want stress, again, that this approach lets you run any tool you need in an environment designed by its maintainers and protects you from any image changes that would potentially break your applications (which could happen, and has happened, with community repositories).
The only thing left is to install the Docker Engine, but I hope you have already done this. And even though it is not necessary, you can also install Docker Compose (we will be using it to make things a bit more readable). Now that we have a clear goal and all that we need to accomplish it – lets get this started :)
Setting up Tomcat
Since we want to test our application once we create it, it makes more sense to set up our web server first. As we set in our requirements this will be Apache Tomcat and we want to use the official Docker Hub repository, so the only thing we have to do is run the following command, which will take the official Tomcat image, run it and bind it’s port 8080 to port 8080 of our Docker environment:
Aaaand that’s it!
Now you should be able to see the Tomcat home page on port 8080 of your Docker environment.
Docker Tomcat Java War
This covers the first part of want we initially set out to do. Let’s see if the second part will be as easy :)
Creating and Packaging a Java application using Maven
Now comes the tricky part. If you are familiar with Maven you know that it is a CLI tool, so how do we use it without having Java installed (we agreed on this earlier)? It turns out there is an official Maven image we can use. To check if we can use it let’s run:
The output you should get is something like this:
Let’s break down the command we just executed. It creates a container and runs the maven command in it. The curious part is the –rm parameter, which will remove the container once the execution of the command is over.
Great! So now that we can run maven in a container let’s create a simple web application. Normally you would do this by running something along the lines of:
If you are not familiar with Maven I suggest you have a look at Maven in 5 Minutes where I took this command from, with the only difference that the architecture type in our case is maven-archetype-webapp. In short, this command will create a new web application project in the DockerExample directory. Knowing how to run maven commands, we can just replace the mvn –version with this command, right? Wrong!
Since the default working directory for the Maven image is /, if we execute:
it will create the new project in /DockerExample inside the container and this will be forever lost once the container is removed. As I mentioned, because of the –rm parameter, this will happen once the execution of the command ends. To overcome this we have to adjust our command a bit and it will look like this:
If you are familiar with Docker you will notice that we mounted the current directory as /external in the container by using -v $(pwd):/external and we changed the current working dir for the container to /external with -w /external. So now when we run the command we should have the new directory DockerExample created in the mounted volume and preserving it after we destroy the container.
Now that we have the project we need to package it. Again, the normal way of doing this is by navigating to the project directory and executing:
We want to run this inside a container, thus we need to mount the folder it is in as a container volume. But first lets navigate to the location of our project, in my case I just go into the DockerTest directory. Since Maven lets us specify the location of the project we have two options to package the project. The first one is to set the working directory the same way we did when we created the new application, using Docker’s -w parameter:
The second option is to run the command using Maven’s -f parameter to specify the location of the pom.xml and it looks like this:
Both commands should create a directory called target in the location of your project and it should contain your build. Since we set the artefact ID to be DockerExample it should be something like this:
Docker Tomcat Java Code
where the DockerExample and DockerExpanded.war are the expanded and archived builds.
And again – this is IT! This is all it takes to package your Java application, easy right? Right!
Docker Hub Tomcat
Now that we have the application is ready let’s serve it with the web server we prepared.
Deploying and serving your Java application
Before we get any further I want to clarify something that puzzled me a lot when I started using Tomcat and that is the app deployment. If you are familiar with Tomcat you can skip to the next paragraph, otherwise – BEHOLD! The way you deploy an application to Tomcat is by simply adding your application build to Tomcat’s webapps directory (in the case of the official Docker image that is /usr/local/tomcat/webapps). And here’s the tricky part – based on the build name added to the webapps directory Tomcat maps this application to a specific route. So if we add our DockerExample.war to the webapps as docker-example.war, we will be able to access it on /docker-example path for the Tomcat url. If you want to deploy your application without any extra paths, you have to place an artefact named ROOT. If this still sounds vague I am sure everything will be clear by the end of this blog post.
Let’s get this started!
If you navigate to your application directory you should be able to serve your application using:
Now if we go to /docker-example on port 8080 for your Docker environment you should be able to the see the output of our basic web application:
But what if we want this to be the our website instead of just one of the many endpoints. As we said earlier, Tomcat requires a ROOT application for that. We can achieve this by modifying our Docker command to look like this:
This should do the trick, but instead gives me the opportunity to use my favourite gif again:
It turns out that the Tomcat image has an exploded artifact deployed as ROOT, so we have to modify our command a bit and it will look like this:
And now you should be getting the proper response:
And our job here is done… except for a couple of finishing touches.
This part is not really necessary, but if you have Docker Compose installed you could serve the application by using the following docker-compose.yml:
Once you have this you can create a simple shell script to package and serve your application.
This is it!
We can now create, build and serve our Maven application, using only the official Docker images for Maven and Tomcat.
You can find the source code for this post here https://github.com/mikechernev/dockerised-java
P.S.: Not great for development, perfect for testing things out
Docker Tomcat Java Application
As you can imagine this setup is not going to be very useful for local development, since you don’t have an easy way to debug your application (or I still haven’t figured out one). But this is a great way to quickly package and serve your application on a VPS, which is what I initially did this for. It is also a fast and easy way to test out new applications on a clean environment. That’s why I won’t suggest using this setup for local development, but I highly recommend it for deploying your application.