Java WatchService Example to Auto Reload Properties

Automatically refresh the configuration files whenever any change happens in those files – It is a very common problem seen in the most application. Every application has some configuration which is expected to be refreshed on every change in the configuration file. Past approaches to solve this problem had consisted of having a Thread, which periodically poll for file change based on the ‘last update timestamp‘ of the configuration file.

Now with Java 7, things have changed. Java 7 has introduced an excellent feature: WatchService. I will try to give you a potential solution to the above problem. This may not be the best implementation, but it will surely give a very good start for your solution. I bet !!

Table of Contents:

1) A brief overview of WatchService
2) Writing our configuration provider
3) Introducing configuration change listener
4) Testing our code
5) Key notes

1. Java WatchService API

A WatchService is the JDKs internal service which watches for changes on registered objects. These registered objects are necessarily the instances of Watchable interface. When registering the watchable instance with WatchService, we need to specify the kind of change events we are interested in.

There are four type of events as of now:

  1. ENTRY_CREATE,
  2. ENTRY_DELETE,
  3. ENTRY_MODIFY, and
  4. OVERFLOW.

You can read about these events in provided links.

WatchService interface extends Closeable interface, means service can be closed as and when required. Normally, it should be done using JVM provided shut down hooks.

2. Application Configuration Provider

A configuration provider is simply a wrapper for holding the set of properties in java,util.Properties instance. It also provides methods to get the configured properties using their KEY.

package testWatchService;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class ApplicationConfiguration {
	private final static ApplicationConfiguration INSTANCE = new ApplicationConfiguration();

	public static ApplicationConfiguration getInstance() {
		return INSTANCE;
	}

	private static Properties configuration = new Properties();

	private static Properties getConfiguration() {
		return configuration;
	}

	public void initilize(final String file) {
		InputStream in = null;
		try {
			in = new FileInputStream(new File(file));
			configuration.load(in);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public String getConfiguration(final String key) {
		return (String) getConfiguration().get(key);
	}

	public String getConfigurationWithDefaultValue(final String key,
			final String defaultValue) {
		return (String) getConfiguration().getProperty(key, defaultValue);
	}
}

3. Configuration Change Listener – File Watcher

Now when we have our basic wrapper for our in-memory cache of configuration properties, we need a mechanism to reload this cache on runtime, whenever configuration file stored in file system changes.

I have written a sample working code for your help:


package testWatchService;

import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class ConfigurationChangeListner implements Runnable {
	private String configFileName = null;
	private String fullFilePath = null;

	public ConfigurationChangeListner(final String filePath) {
		this.fullFilePath = filePath;
	}

	public void run() {
		try {
			register(this.fullFilePath);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void register(final String file) throws IOException {
		final int lastIndex = file.lastIndexOf("/");
		String dirPath = file.substring(0, lastIndex + 1);
		String fileName = file.substring(lastIndex + 1, file.length());
		this.configFileName = fileName;

		configurationChanged(file);
		startWatcher(dirPath, fileName);
	}

	private void startWatcher(String dirPath, String file) throws IOException {
		final WatchService watchService = FileSystems.getDefault()
				.newWatchService();
		Path path = Paths.get(dirPath);
		path.register(watchService, ENTRY_MODIFY);

		Runtime.getRuntime().addShutdownHook(new Thread() {
			public void run() {
				try {
					watchService.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});

		WatchKey key = null;
		while (true) {
			try {
				key = watchService.take();
				for (WatchEvent<?> event : key.pollEvents()) {
					if (event.context().toString().equals(configFileName)) {
						configurationChanged(dirPath + file);
					}
				}
				boolean reset = key.reset();
				if (!reset) {
					System.out.println("Could not reset the watch key.");
					break;
				}
			} catch (Exception e) {
				System.out.println("InterruptedException: " + e.getMessage());
			}
		}
	}

	public void configurationChanged(final String file) {
		System.out.println("Refreshing the configuration.");
		ApplicationConfiguration.getInstance().initilize(file);
	}
}

Above class is created using a thread which will be listening to configuration properties file changes using WatchService.

Once, it detects any modification in the file, it simply refreshes the in-memory cache of configuration.

The constructor of the above listener takes only one parameter i.e. fully qualified path of the monitored configuration file. Listener class is notified immediately when configuration file is changed in file system.

This listener class then call ApplicationConfiguration.getInstance().initilize(file); to reload in memory cache.

4. Testing our code

Now, when we are ready with our classes, we will test them.

First of all, store a test.properties file with following content in 'C:/Lokesh/temp' folder.

TEST_KEY=TEST_VALUE

Now, let us test above classes using below code.

package testWatchService;

public class ConfigChangeTest {
	private static final String FILE_PATH = "C:/Lokesh/temp/test.properties";

	public static void main(String[] args) {
		ConfigurationChangeListner listner = new ConfigurationChangeListner(
				FILE_PATH);
		try {
			new Thread(listner).start();
			while (true) {
				Thread.sleep(2000l);
				System.out.println(ApplicationConfiguration.getInstance()
						.getConfiguration("TEST_KEY"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

//Output of the above program (Change the TEST_VALUE to TEST_VALUE1 and TEST_VALUE2 using any file editor and save).

Refreshing the configuration.

TEST_VALUE

TEST_VALUE

TEST_VALUE

Refreshing the configuration.

TEST_VALUE1

Refreshing the configuration.

TEST_VALUE2

Above outputs show that every time we make any change to the property file, properties loaded are refreshed and new property value is available to use. Good work is done so far !!

5. Key notes

  1. If you are using Java 7 in your new project, and you are not using old-fashioned methods to reload your properties, you are not doing it rightly.
  2. WatchService provides two methods take() and poll(). While take() method wait for next change to happen and until it is blocked, poll() immediately check for change event.

    If nothing changed from last poll() call, it will return null. poll() method does not block the execution, so should be called in a Thread with some sleep time.

Drop me your questions/ suggestion in the comments area.

Happy Learning !!

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

8 thoughts on “Java WatchService Example to Auto Reload Properties”

  1. Hi,
    why just I can’t use a thread/timer checking every second for properties file lastModified attribute and reload the properties if there are changes?
    What’s wrong with that?

    Reply
  2. Hi,
    It’s very useful for properties. But my configuration is using json.config. ( “TEST_KEY”: TEST_VALUE). Can you please help me to do same.

    Reply
  3. Hi! It’s very useful article for me, thank you!
    But I have some question. Would be grateful for the answer)

    Are “while (true)” is a good choice to cycle here?
    Is thread exist after close program (Spring-boot in my case)?
    Mb we must write something like this “while(Application.isActive())”?

    Reply
  4. This was very helpful to me in 2014:)
    One thing that is nice about WatchService is that it extends Closeable, so it can be initiated with try-with-resources. And then you would not need the Runtime.getRuntime().addShutdownHook(new Thread()…

    Reply
  5. Thanks for the great article! One correction, you’ll want to call “configuration.clear()” before “configuration.load(in)”, load doesn’t clear out the existing keys so if you remove a key from your properties file it will still exist during the reloading of the properties unless you clear the existing values.

    Reply

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.