It’s unimaginable how many apps still force its user to manually update — they’re very annoying! Fortunately if your app is a Java Web App (eg: running in Tomcat) you can set it up to auto-update silently. This is very helpful if you deploy your web-app onsite on clients’ infrastructure.
This article assumes you have a basic understanding of Spring MVC. The article is written against Spring Framework version 4.0.3.RELEASE.
The UpdaterService
The first thing to setup is an UpdaterService that checks periodically if a new version is available. You will need a TaskScheduler to schedule the periodic job. Feel free to either use Java annotation or XML style:
// Java annotation g public class AppConfig { ... }
Once setup, you can decorate a method with annotation and it will be invoked by the scheduler
thread every specified period.
In below example my UpdaterService class has a checkAndInstallUpdate() method that get invoked by the scheduler thread every 8 hours.
public class UpdaterService { (fixedDelay=8*3600) public void checkAndInstallUpdate() { } }
I will not go into much detail on how to check and download the update. There are tons of way of doing it and one popular method is to use Apache HttpClient library.
Typically you would upload your war/jar artifact in your website and have a REST service for apps to check if new version is available. If so then download and install it.
Self Executing Tomcat Container
Here’s another trick I use on my self-updating app: use a self-executing Tomcat. Basically everything required to run tomcat is packaged inside a nested war/jar. If you use spring-boot it already have a support for this, but otherwise you can set it up painlessly using maven plugin.
The other thing you need to make sure is the war/jar file isn’t file-system-locked when the app is running so it can be overwritten by the new version. Self-executing Tomcat does not lock the jar.
Restarting Tomcat
Here comes the tricky bit. First use ProcessBuilder to spawn a new OS process
String myAppPath = System.getenv("MY_APP_PATH"); String command = myAppPath + File.separator + "run.bat"; ProcessBuilder builder = new ProcessBuilder(command); builder.start();
In the example above it is assumed you have a windows script called run.bat and the value of environment variable MY_APP_PATH is set to the folder that contain the run script.
And directly after that, terminate the JVM using System.exit(0)
System.exit(0);
The termination of the old process will not affect the new ones. And yes, you need to start the new process before you terminate the old one, otherwise it wouldn’t have chance to do so.
See Also
- Stack Overflow Q&A about restarting Java program: http://stackoverflow.com/a/4194224/179630