Two weeks ago, we studied how to replace desktop Java apps with Java webapps. Now is the time to think about distributing such desktop webapps.
The current trend now is to use Docker. I assume readers are at least familiar with the technology. The most straightforward way is to create a WAR and deliver it inside a Tomcat image. Another option is to create a fat JAR with Tomcat embedded as per the previous post, and run it inside a image with the JRE only.
One of the deciding factors is the size of the resulting image. Interestingly enough, the second option is the best choice, as Tomcat embedded is a stripped down version of the regular distribution.
About size
To obtain the smallest image, one should favor Alpine Linux-based containers over more complete Linux distributions (such as Debian). To slim down the image even further, use jlink, which is part of Java 9. Unfortunately, at the time of this writing, there’s no JRE 9 image available for Alpine. Another way to shave off a few kilobytes is to replace embedded Tomcat by Jetty: pom.xml
|
A possible Dockerfile might look like this:
FROM openjdk:8-jre-alpine
ARG JAR_FILE
ADD ${JAR_FILE} /usr/share/renamer.jar
ENTRYPOINT ["/usr/bin/java", "-jar", "/usr/share/renamer.jar"]
With it, it’s possible to create an image of relatively acceptable size:
nfrankel/renamer 0.0.1-SNAPSHOT f8ed5f177771 38 minutes ago 113MB
For Maven integration, I use the dockerfile-maven-plugin from Spotify. Here’s an example how to use it:
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<ecution>
<id>default</id>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<configuration>
<repository>nfrankel/renamer</repository>
<tag>${project.version}</tag>
<buildArgs>
<JAR_FILE>target/${project.build.finalName}.${project.packaging}</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
Once the image has been built, it’s trivial to launch the container:
docker run -ti -p 8080:8080 -v /tmp/foo:/tmp/foo nfrankel/renamer:0.0.1-SNAPSHOT
It’s pretty common to map the port. If the application needs to interact with the filesystem - as some (most?) desktop apps do, volumes need to be mapped as well. |
At this point, all is set to launch the app. However, a new question creeps in: "How to launch a browser in Docker?"
Opening a browser on the host system is not possible from Docker. The way to go is to install a browser in the image, launch it and display it on the host through X11. However, there are a couple of drawbacks to that:
- It’s not trivial.
On my Mac, it requires:
- Installing dedicated software for X11 emulation (XQuartz)
- Configuration of said software
- Pass additional parameters for Docker to run:
docker run -ti -e DISPLAY=192.168.1.73:0 -p 8080:8080 -v /tmp/.X11-unix:/tmp/.X11-unix nfrankel/renamer:0.0.1-SNAPSHOT
- It increases the size of the image a lot!
In the end, it’s better to let it out of the image and create simple multi-platform scripts to achieve the same.
The SonarQube distribution is a good example. It offers scripts for the following OS:
|
Conclusion
Docker is a good way to distribute servlet-embedded webapps. Yet, it comes with a not-that-good desktop integration as it’s not straightforward to interact with the host system.