Auto refreshing properties from property files

13 Oct

Source code available @
svn checkout https://whiteboardjunkie.googlecode.com/svn/trunk/properties-dynaload properties-dynaload
Browse Source code here

I have written a tool to automatically reload a property file on file change. This tool came very handy when implementing stand alone server applications that cannot resort to a server restart for refreshing properties. The class itself is a simple construct that spawns a LOW_PRIORITY thread to monitor the property file and reload the properties when the file has changed. To address some specific use cases where the system should be notified of such a reloading of properties I have also provided a mechanism to add listeners. In the following sample snippet an instance of AutoRefreshingProperties is instantiated against ‘config/configuration.properties’. We are also adding two listeners who are interested to know of any file changes.

	static final File destFile = new File("target/test-classes/config/configuration.properties");
	static Properties properties = null;
	@BeforeClass
	public static void beforeClass() throws Exception{
		properties = new AutoRefreshingProperties(null,destFile.getAbsolutePath())
					.addListener(new FileChangeAdapter() {
						@Override
						public void notifyFileChanged(Properties properties) {
							System.out.println("LISTENER1 : Properties have changed...");
							System.out.println("my.property1=" + properties.getProperty("my.property1"));
						}
					})
					.addListener(new FileChangeAdapter() {
						@Override
						public void notifyFileChanged(Properties properties) {
							System.out.println("LISTENER2: Properties have changed...");
							System.out.println("my.property1=" + properties.getProperty("my.property1"));
						}
					});
	}

To test whether the gig really works we are then changing the contents of the properties file by copying over another properties file to the same location. From my experience I can relate to this test case very well since there are dozens of instances where restarting a running server is not an option to change a simple property.

		copyNewPropertiesFile("target/test-classes/master-config/configuration1.properties");
		Thread.sleep(4);
		assertEquals("foo1", properties.getProperty("my.property1"));
	}
	private synchronized void copyNewPropertiesFile(String fromPath)throws Exception{
		FileUtils.copyFile(new File(fromPath), destFile);
	}

The resulting output clearly shows that the test passes, the properties have changed and both the notifiers are notified.

/*
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.boni.dynaload.TestDynamicPropertyLoading
LISTENER1 : Properties have changed...
my.property1=foo
LISTENER2: Properties have changed...
my.property1=foo
LISTENER1 : Properties have changed...
my.property1=foo1
LISTENER2: Properties have changed...
my.property1=foo1
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
*/

The source code that did this auto loading is not very complex either. It extends the java.util.Properties and adds a new constructor that accepts an absolute file path as the location of the property file that needs auto loading. This suffices the problem I am trying to solve since it is highly unlikely that one needs to change a property file embedded in a jar file. More often than not a fix folder (say ‘config’) houses the configuration files and this folder is added to the classpath.

class AutoRefreshingProperties extends Properties{
	private long LAST_LOADED = System.currentTimeMillis();
	private List listeners = Collections.synchronizedList(new ArrayList());
	public AutoRefreshingProperties(String absolutePathToPriopertiesFile) throws IOException{
		this(null, absolutePathToPriopertiesFile);
	}
	public AutoRefreshingProperties(Properties defaults, String absolutePathToPriopertiesFile) throws IOException{
		super(defaults);
		load(absolutePathToPriopertiesFile);
	}
	public AutoRefreshingProperties removeListener(FileChangeListener listener){
		listeners.remove(listener);
		return this;
	}
	public AutoRefreshingProperties addListener(FileChangeListener listener){
		listeners.add(listener);
		return this;
	}
	public synchronized void load(String absoluteFilePath) throws IOException {
		InputStream is = new FileInputStream(new File(absoluteFilePath));
		this.load(is);
		LAST_LOADED = System.currentTimeMillis();
		keepUpdated(absoluteFilePath);
	}
	abstract class FileWatcher implements Runnable{} 
	private void keepUpdated(final String absoluteFilePath){
		Executors.newSingleThreadExecutor().submit(
				new FileWatcher(){
					@Override
					public void run() {
						try {
							while(true){
								if (new File(absoluteFilePath).lastModified() > LAST_LOADED){
									InputStream is =  new FileInputStream(new File(absoluteFilePath));
									load(is);
									LAST_LOADED = System.currentTimeMillis();
									notifyListeners();
								}
								Thread.yield();
								Thread.sleep(2000);
							}
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
		);
	}
	private void notifyListeners(){
		final Properties properties = new Properties(this);
		for (FileChangeListener aListener : listeners){
			aListener.notifyFileChanged(properties);
		}
	}
}

In the simplest of usage scenarios all you will need to do the auto loading feature is to just create an instance of AutoRefreshingProperties by passing the path to the properties file. The class will take care of monitoring the file and reloading itself on file change. So at any point of tile when the system makes a call to Properties.getProperty(…) it will return the latest value specified in the properties file. As I have shown in the test case the power of this tool can be extended by attaching listeners and taking appropriate reactive actions on property changes.

The feature that I would love to see augmented to such a solution is how a spring bean property configured through PropertyPlaceholderConfigurer can load an refresh the beans on property file changes. In my opinion that solution will not be as trivial as the one I cooked up here. Having said that one can very easily use the notification listener mechanism to do whatever it needs to do on such an event.

JDK7 has a WatchService specificatiion. The b64 release has this interface in place. Such a support from JVM will alleviate a lot of thread management issues that each of us have cooked up for file and directory monitoring.

About these ads

14 Responses to “Auto refreshing properties from property files”

  1. Ken October 15, 2009 at 11:16 pm #

    This would not be good in a JEE environment where you are not supposed to manage your own threads.

    Also, it’s silly to create a single thread executor and pass it threads. First, you can just pass runnables to executors. Second, you’re only ever passing one runnable to the executor. You should just replace that code with

    new Thread() { .. }.start();

    Look at how Apache commons-configuration does it with their FileChangedReloadingStrategy. Maybe you could just use commons-configuration in your applications when you need this functionality.

    You should also test to see if removing properties from your file results in the property being removed from the AutoRefreshingProperties object.

    • bonigv October 16, 2009 at 7:28 am #

      Ken, thanks for the comments.

      Using a thread ExecutorService comes out of habit. I know from a lot of concurrency solution experience that the Executor Service managed threads are much better behaved. Esepecially in this scenario where it is watching a file.

      Which JEE environment do you feel might pose a problem? Most of the JEE app servers provide a mechanism to use a TimerTask kind of construct. I am pretty sure that piggybacking on such mechanism will let us use this class and then a listener to update the properties to the JNDI tree. Do you see any specific challenges in this approach? I agree that spawning this class in an Ejb instance will not be very appropriate because of the lifecycle controls the container has to do and a hanging thread will mess with the life cycle management.

      Using Runnable in place of Thread : I think in general it is a good idea because we can take away some memory over heads. Since Thread is a runnable the currentl implementation is not a bad code though. I think I must have used thread just because I was lazy to create a new Impl :-)

      Thanks for pointing out the Apache commons configuration. I just hate reinventing a wheel. Looks like I just did one. My bad!!

      And finally, since the monitor is a dumb thread that watches for file changes through modification date it works (tested too) when properties are removed from the file.

    • bonigv October 17, 2009 at 2:31 pm #

      Ken, I have taken your suggestion and replaced the Thread with a Runnable. Makes sense! thanks..

  2. Dave October 16, 2009 at 4:53 am #

    We have been using the Apache Commons Configuration library in production for several years. Here is a link to their docs (specifically the automatic reloading section)”

    http://commons.apache.org/configuration/userguide/howto_filebased.html#Automatic_Reloading

  3. Dean October 16, 2009 at 5:00 am #

    You should name your thread and the thread should be a daemon.

    Also, Ken is right, you should be passing Runnable object rather than a Thread since it is misleading.

  4. David Seymore October 16, 2009 at 8:43 am #

    JMX & Preferences API.

    Everything should be coded to have working defaults, and allow runtime configuration changes.

  5. Tamas October 16, 2009 at 5:02 pm #

    1. The ‘synchronize’ keyword does nothing on the thread for the following reason:

    synchronized in the ‘run’ will lock the object itself (which is the anonymous Thread object), nobody else try to get the thread monitor for that object. (thus the public load() method can be executed paralelly.) I suppose you put the synchronized keyword there for this reason. Looping forever in a synchronized section is somewhat unusual.

    2. In the watch thread you loop forever. You should at least add a Thread.yield() or even better some sleep(). Even if it is low prio, it can consume lots of cpu power. And who cares if you reload after 2-300ms only.

    Cheers,

    Tamas

    • bonigv October 16, 2009 at 5:52 pm #

      Tamas, you are absolutely correct. Both points are very valid. I will update the code. Thanks!

  6. ironclay October 16, 2009 at 8:08 pm #

    Actually you need to synchronize access to any of the properties methods when you’re updating the properties object since its not inherently synchronized. I think the lazy reload approach taken by the Apache folks is better if you’re in a development environment whereas your approach is more pro-active and makes more sense in a production environment (why make the caller wait for the properties to be reloaded).

    You should consider implementing a locking mechanism to prevent concurrent access to the properties object. You might want to consider implementing a Proxy to the underlying Properties object.

  7. atar October 31, 2009 at 7:02 am #

    nice, i wanna try this autorefresh property..

  8. Lukasz October 28, 2011 at 4:21 pm #

    You should also add a shut down hook…

  9. Admin October 15, 2012 at 9:44 pm #

    http://howtodoinjava.com/2012/10/10/auto-reload-of-configuration-when-any-change-happen/

    This article also try to solve this problem differently. Please review.

Trackbacks/Pingbacks

  1. Refrescando las propiedades desde un fichero properties » Blog de Ruben Garcia - October 15, 2009

    [...] Gracias a la solucion del Blog de Boni Golapan podemos autorefrescar los objetos cuando el fichero properties cambie gracias a la solucion que nos ofrece [...]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: