7 min read

Containers are changing the way we build and deliver software. They are also the essential glue for DevOps and the way to take CI/CD to another level. Put them together and you will have one of the most powerful environments in IT. But can Java EE take advantage of it? Of course! If an application server is an abstraction of Java EE applications, containers are an abstraction of the server, and once you have them built into a standard such as Docker, you have the power to use such tools to manage an application server.

This article is an extract from the book Java EE 8 Cookbook, authored by Elder Moraes.
This article will show you how to put your Java EE application inside a container. Since day one, Java EE has been based on containers. If you doubt it, just have a look at this diagram:
JavaEE Containers
Java EE architecture: https://docs.oracle.com/javaee/6/tutorial/doc/bnacj.html

It belongs to Oracle’s official documentation for Java EE 6 and, actually, has been much the same architecture since the times of Sun.

If you pay attention, you will notice that there are different containers: a web container, an EJB container, and an application client container. In this architecture, it means that the applications developed with those APIs will rely on many features and services provided by the container.

When we take the Java EE application server and put it inside a Docker container, we are doing the same thing— it is relying on some of the features and services provided by the Docker environment.

This recipe will show you how to deliver a Java EE application in a container bundle, which is called an appliance.

Installing Docker

First, of course, you need the Docker platform installed in your environment. There are plenty of options, so I suggest you check this link and get more details:

And if you are not familiar with Docker commands, I recommend you have a look at this beautiful cheat sheet:

You’ll also need to create an account at Docker Hub so you can store your own images. Check it out.

It’s free for public images.

Building Java EE Container

To build your Java EE container, you’ll first need a Docker image. To build it, you’ll need a Dockerfile such as this:

FROM openjdk:8-jdk
ENV GLASSFISH_HOME /usr/local/glassfish
ENV PATH ${GLASSFISH_HOME}/bin:$PATH
ENV GLASSFISH_PKG latest-glassfish.zip
ENV GLASSFISH_URL https://download.oracle.com/glassfish/5.0/nightly/latest-glassfish.zip

RUN mkdir -p ${GLASSFISH_HOME}

WORKDIR ${GLASSFISH_HOME}

RUN set -x 
&& curl -fSL ${GLASSFISH_URL} -o ${GLASSFISH_PKG} 
&& unzip -o $GLASSFISH_PKG 
&& rm -f $GLASSFISH_PKG 
&& mv glassfish5/* ${GLASSFISH_HOME} 
&& rm -Rf glassfish5

RUN addgroup glassfish_grp 
&& adduser --system glassfish 
&& usermod -G glassfish_grp glassfish 
&& chown -R glassfish:glassfish_grp ${GLASSFISH_HOME} 
&& chmod -R 777 ${GLASSFISH_HOME}
COPY docker-entrypoint.sh /
RUN chmod +x /docker-entrypoint.sh

USER glassfish

ENTRYPOINT ["/docker-entrypoint.sh"]

EXPOSE 4848 8080 8181
CMD ["asadmin", "start-domain", "-v"]

This image will be our base image from which we will construct other images in this chapter. Now we need to build it:

docker build -t eldermoraes/gf-javaee-jdk8 .

Go ahead and push it to your Docker Registry at Docker Hub:

docker push eldermoraes/gf-javaee-jdk8

Now you can create another image by customizing the previous one, and then put your app on it:

FROM eldermoraes/gf-javaee-jdk8
ENV DEPLOYMENT_DIR ${GLASSFISH_HOME}/glassfish/domains/domain1/autodeploy/

COPY app.war ${DEPLOYMENT_DIR}

In the same folder, we have a Java EE application file (app.war) that will be deployed inside the container. Check the See also section to download all the files.

Once you save your Dockerfile, you can build your image:

docker build -t eldermoraes/gf-javaee-cookbook .

Now you can create the container:

docker run -d --name gf-javaee-cookbook 
    -h gf-javaee-cookbook 
    -p 80:8080 
    -p 4848:4848 
    -p 8686:8686 
    -p 8009:8009 
    -p 8181:8181 
    eldermoraes/gf-javaee-cookbook

Wait a few seconds and open this URL in your browser:

http://localhost/app

How to work with Dockerfile

Let’s understand our first Dockerfile:

FROM openjdk:8-jdk

This FROM keyword will ask Docker to pull the openjdk:8-jdk image, but what does it mean?

It means that there’s a registry somewhere where your Docker will find prebuilt images. If there’s no image registry in your local environment, it will search for it in Docker Hub, the official and public Docker registry in the cloud.

And when you say that you are using a pre-built image, it means that you don’t need to build, in our case, the whole Linux container from scratch. There’s already a template that you can rely on:

ENV GLASSFISH_HOME /usr/local/glassfish
ENV PATH ${GLASSFISH_HOME}/bin:$PATH
ENV GLASSFISH_PKG latest-glassfish.zip
ENV GLASSFISH_URL https://download.oracle.com/glassfish/5.0/nightly/latest-glassfish.zip
RUN mkdir -p ${GLASSFISH_HOME}

WORKDIR ${GLASSFISH_HOME}

Here are just some environment variables to help with the coding.

RUN set -x 
  && curl -fSL ${GLASSFISH_URL} -o ${GLASSFISH_PKG} 
    && unzip -o $GLASSFISH_PKG 
    && rm -f $GLASSFISH_PKG 
  && mv glassfish5/* ${GLASSFISH_HOME} 
  && rm -Rf glassfish5

The RUN clause in Dockerfiles execute some bash commands inside the container when it has been created. Basically, what is happening here is that GlassFish is being downloaded and then prepared in the container:

RUN addgroup glassfish_grp 
    && adduser --system glassfish 
    && usermod -G glassfish_grp glassfish 
    && chown -R glassfish:glassfish_grp ${GLASSFISH_HOME} 
    && chmod -R 777 ${GLASSFISH_HOME}

For safety, we define the user that will hold the permissions for GlassFish files and processes:

COPY docker-entrypoint.sh /
RUN chmod +x /docker-entrypoint.sh

Here we are including a bash script inside the container to perform some GlassFish administrative tasks:

#!/bin/bash
if [[ -z $ADMIN_PASSWORD ]]; then
ADMIN_PASSWORD=$(date| md5sum | fold -w 8 | head -n 1)
echo "##########GENERATED ADMIN PASSWORD: $ADMIN_PASSWORD
##########"
fi

echo "AS_ADMIN_PASSWORD=" > /tmp/glassfishpwd
echo "AS_ADMIN_NEWPASSWORD=${ADMIN_PASSWORD}" >> /tmp/glassfishpwd

asadmin --user=admin --passwordfile=/tmp/glassfishpwd change-admin-password --domain_name domain1
asadmin start-domain

echo "AS_ADMIN_PASSWORD=${ADMIN_PASSWORD}" > /tmp/glassfishpwd

asadmin --user=admin --passwordfile=/tmp/glassfishpwd enable-secure-admin
asadmin --user=admin stop-domain
rm /tmp/glassfishpwd

exec "$@"

After copying the bash file into the container, we go to the final block:

USER glassfish
ENTRYPOINT ["/docker-entrypoint.sh"]

EXPOSE 4848 8080 8181
CMD ["asadmin", "start-domain", "-v"]

The USER clause defines the user that will be used from this point in the file. It’s great because from there, all the tasks will be done by the glassfish user.

The ENTRYPOINT clause will execute the docker-entrypoint.sh script.

The EXPOSE clause will define the ports that will be available for containers that use this image.

And finally, the CMD clause will call the GlassFish script that will initialize the container.

Now let’s understand our second Dockerfile:

FROM eldermoraes/gf-javaee-jdk8

We need to take into account the same considerations about the prebuilt image, but now the image was made by you. Congratulations!

ENV DEPLOYMENT_DIR ${GLASSFISH_HOME}/glassfish/domains/domain1/autodeploy/

Here, we are building an environment variable to help with the deployment. It’s done in the same way as for Linux systems:

COPY app.war ${DEPLOYMENT_DIR}

This COPY command will literally copy the app.war file to the folder defined in the DEPLOYMENT_DIR environment variable.

From here, you are ready to build an image and create a container. The image builder is self-explanatory:

docker build -t eldermoraes/gf-javaee-cookbook .

Let’s check the docker run command:

docker run -d --name gf-javaee-cookbook 
    -h gf-javaee-cookbook 
    -p 80:8080 
    -p 4848:4848 
    -p 8686:8686 
    -p 8009:8009 
    -p 8181:8181 
    eldermoraes/gf-javaee-cookbook

If we break it down, this is what the various elements of the command mean:

  • -h: Defines the host name of the container.
  • -p: Defines which ports will be exposed and how it will be done. It is useful, for example, when more than one container is using the same port by default—you just use them differently.
  • eldermoraes/gf-javaee-cookbook: The reference to the image you just built.

So now you’ve successfully built a container for your Java EE application, in Docker. If you found this tutorial helpful and would like to learn more, head over to the Packt store and get the book Java EE 8 Cookbook, authored by Elder Moraes.

Read Next:

Oracle announces a new pricing structure for Java

Design a RESTful web API with Java [Tutorial]

How to convert Java code into Kotlin

I'm a technology enthusiast who designs and creates learning content for IT professionals, in my role as a Category Manager at Packt. I also blog about what's trending in technology and IT. I'm a foodie, an adventure freak, a beard grower and a doggie lover.

LEAVE A REPLY

Please enter your comment!
Please enter your name here