Today we will see how Build, Run, Deploy and Modify in immediate mode a simple Java App deployed on Tomcat via the Docker Compose Watch feature.
The purpose of this article is not to introduce the development of some Tomcat App via Spring or whatever platform but instead of that to explain how we can use the Docker's tools to make easier it.
đź’ˇA Docker Compose stack is ready to run on my github.com/ulrich space available in the repository : https://github.com/ulrich/develop-java-app-with-compose-watch
Docker Compose Watch in few words
This tool has been released in the Docker Compose v2.22.0 by my favorite ❤️ team at Docker. Quickly when you use Compose during your developments, it can useful to synchronize your local changes with your deployed App running in his container. Before the Watch feature you were forced to use a workaround like Bind mount for getting a hot reloaded capability and it was OK. But in many cases in many technologies, it's not a good idea to work like this because for example in Java (in particular Java WAR App based model) technology, we don't want to deploy our App after a simple modification. You understood, a Java WAR App needs many resources which can slow down the inner loop development experience.
Docker Compose Watch offers an another approach for developers by using of new properties allowing to synchronize with a fine-grained level each piece of your App. By the way, a couple of new commands (develop/watch/action) will help you to define what/when/how I have to synchronize one piece of code (file, directory, module...).
You will find more information in the following source here.
Craft a simple Java WAR App
Add a simple Spring Boot app with the convenience dependencies.
The best way, for generating a ready-to-run application in Java you can use the Spring Boot Initializr tool.
We will need to enroll the following dependencies:
- spring-boot-starter-tomcat
- spring-boot-starter-web
- spring-boot-starter-thymeleaf
- spring-boot-starter-data-jpa
These followings for this introduction will use Java 21 and Maven.
There is nothing of special to be noted for this application which use the classic architecture from Java App. The data layer is fed by a pre-initialized Postgres Compose service.
After running the Docker Compose services you will access to the students.html
page from the following URL : http://localhost:8080/app-for-compose-watch-0.0.1-SNAPSHOT/students/all
(figure 1)
Run the App with --watch flag
In this introduction, we will see two ways to synchronize our application.
For running the App with the Compose Watch enabled, we have to launch like this :
❯ docker compose up --build --watch
This command will build and run the App with the Compose Watch observer enabled (figure 2).
đź‘‹ This way to start the services is interesting because in this case, we ask to build, run and watch the expected service with the logs enabled in the console.
 When I want to create or modify the Java code
In Java WAR App based on Tomcat, the easiest way to test the inner-loop development is to instruct Compose Watch for rebuilding and deploying the App when a "witness" file has changed. The following Compose configuration allows this development round-trip :
app:
image: app:latest
build: ./
develop:
watch:
- action: rebuild
path: deploy.watch
ports:
- 8080:8080
The notable point to consider there is the path instruction used to trigger a rebuild action when the developer considers it. As I explained previously, we don't want to trigger a rebuild action for each Java file modifications, so we can consider that when we want to test our modifications, the developer have to update this spy file.
For my part I use a IntelliJ Shell script which update the deploy.watch file located in the root path with this kind of configuration (figure 3).
When the Deploy App command is triggered, we can see the Docker Compose Watch feature take the hand and making the expected actions (build, deploy and restart Tomcat). PTAL the following sample of logs after executing the command (figure 4).
Practical and inexpensive!
 When I want to modify a HTML file
The solution to synchronize not compiled sources like Thymeleaf files (for example) is to use the sync action. When we use this feature the Docker Compose doesn't restart the App but only copy and past the files denoted by the couple path/targer. Let's take a look of the following configuration used to (hot) update the students.html file located in src/main/resources/templates/
path.
app:
image: app:latest
build: ./
develop:
watch:
- action: rebuild
path: deploy.watch
- action: sync
path: src/main/resources/templates/
target: /usr/local/tomcat/webapps/app-for-compose-watch-0.0.1-SNAPSHOT/WEB-INF/classes/templates/
ports:
- 8080:8080
The main difference with the previous declaration is the necessity to declare a target path.
Please take a look to the following resource : /students/adults (figure 5).
If I modify the template by adding some information, the modification should instantly happened (figure 6).
 Conclusion
Finally we can consider the Docker Compose Watch tool while developing a Java WAR App on Tomcat. Given that I think is a good alternative from the classical development stack like IntelliJ Plugin or others. All parts of the inner-loop developments can be make in the image itself and no need to install third party tooling.
Have a good day.
Image par jacqueline macou de Pixabay
Top comments (3)
I've read about --watch months ago and didn't see his added value for me, perhaps can you give me a clue : I'm coding with php, js, css and also bash i.e. don't need any kind of generation.
So, since years, I'm using shared volumes between my host and the container and it's fine.
When I need to deploy my docker image, then I build one. My dockerfile has been created with multistages in mind and allow to choice which target I want (prod or dev). Depending on the target, I have a COPY verb or not.
So, in my use case, do you see a path where --watch can be useful to me ?
Thanks !
Hello @cavo789 from my point of view, the bind mount feature is for me a "hack" which wasn't planed at origin for the development round-trip. The watch command (flag) is by design dedicated to the development process. Indeed, with the watch command you have a fine-grained control of files synchronization btw the local system and the container. An another interesting point for my usage (Tomcat + Java) is I can control the level of action required. For example when a class file is modified I need to sync+restart the Tomcat server. Unlike, when I modified a HTML file I just need to sync the file.
For terminating, I don't have very accurate information about the subject but underlying we can hope having better performance during a sync file with watch command (use inotify) because the IOs aren't managed in the same way from the bind mount.
Thanks @ulrich for the reply.