Updating your Java server in runtime

It is sometimes convenient to have the ability to update an application in runtime. You might want to change the log level to track down some bug or maybe change the setting of a feature toggle. But it’s difficult to redeploy the application without disturbing your users. What to do? At SpeedLedger we decided to put the properties we want to change in runtime in a file on disk outside of the application. We then let our application watch the file for changes using Java’s java.nio.file.WatchService. So whenever a property in the watched file is changed the application is automatically called to update its state. We currently use it to add new endpoints in our traffic director.

Doing this first appeared to be really simple, the functionality is built in to Java since version 7. But doing it in a stable and controlled way required some thought, you cannot have your file watch service crash your application in production. So we created a small helper class to hide the complexity and decided to open source it. The code is available on GitHub.

To use it, simply add this dependency to your pom (or similar):

<dependency>
   <groupId>com.speedledger</groupId>
   <artifactId>filechangemonitor</artifactId>
   <version>1.1</version>
</dependency>

To create a new file watch you can do something like this when your application starts:

public class MyFileWatcher {

   final static String CONFIG_HOME = "/home/speedledger";
   final static String CONFIG_FILE = "file.json";

   public void init() {
      FileChangeMonitor monitor = new FileChangeMonitor(CONFIG_HOME, CONFIG_FILE, this::updateSettingsFromFile, 30);
      new Thread(monitor).start();
   }

   void updateSettingsFromFile(String directory, String file) throws IOException {
      ObjectMapper mapper = new ObjectMapper();
      String json = new String(Files.readAllBytes(Paths.get(CONFIG_HOME + File.separator + CONFIG_FILE)), "UTF-8");
      List<Endpoint> endpoints = mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, Endpoint.class));

      // Update the endpoints
   }
}

In this example we watch a JSON file containing “endpoint” objects. Whenever someone writes to the file updateSettingsFromFile is called and the endpoints are read from the file and updated. If something goes really wrong, like the disk becomes unavailable or someone deletes the watched directory, the monitor waits for 30 seconds and then tries to restart the watch service.

It is a good idea to validate the data from the file before updating, if someone makes a mistake when editing the file we want to keep the current state and log an error message.

Note that if you run this on OS X you will notice a substantial (a few seconds) delay in the watch. This happens because Java on OS X doesn’t have a native implementation of WatchService. This shouldn’t be a problem as long as your don’t use OS X in production.

The file change monitor is available on GitHub and Maven Central, we hope you will find it useful!

Leave a Reply