Though having written about other configuration management libraries for Java before, I would like to demonstrate another one today: Netflix Archaius.

Archaius offers some nice features like dynamic typed properties, thread-safe operations, an event system for property changes/updates, a JMX MBean to read and update properties and adaptors for a variety of dynamic configuration sources like Amazon DynamoDB, JDBC, URLs and Apache ZooKeeper.

In the following tutorial I’d like to demonstrate how to read and update application configuration properties with Archaius and data sources like property-files, system-properties, JMX and Apache ZooKeeper.

archaious jmx properties 1024x751
Figure 1. Updating Archaius settings with JMX and JConsole

Dependencies

Using Maven we’re adding the following three dependencies to our project’s pom.xml:

  • archaius-core: The Archaius library

  • archaius-zookeeper: Apache ZooKeeper support for Archaius

  • curator-test: Lets us create ZooKeeper instances for test environments with ease

<dependencies>
	<dependency>
		<groupId>com.netflix.archaius</groupId>
		<artifactId>archaius-core</artifactId>
		<version>0.7.4</version>
	</dependency>
	<dependency>
		<groupId>com.netflix.archaius</groupId>
		<artifactId>archaius-zookeeper</artifactId>
		<version>0.7.4</version>
	</dependency>
	<dependency>
		<groupId>com.netflix.curator</groupId>
		<artifactId>curator-test</artifactId>
		<version>1.3.3</version>
	</dependency>
</dependencies>

Sample Application

As we’re going to demonstrate reading configuration files from different sources, we’re adding the following configuration file to our project (src/main/resources):

config.properties

search.url=https://duckduckgo.com/?q=

And this is our application in a single class.

What is happening here?

  1. We’re starting a new ZooKeeper instance for demonstration purpose

  2. We’re initializing the ZooKeeper client and we’re setting a value ‘http://www.test.de___for the znode path _/config/search.url

  3. We’re setting a system property that allows us to modify configuration values using JMX and the Netflix MBeans

  4. We’re initializing different configuration sources: a source for our property file, a source for reading system properties, a source for ZooKeeper

  5. We’re adding all these configuration sources to a combined configuration, our final configuration. Please note, that the order of the configuration sources is important as it defines the way, we’re searching for specific configuration values from the sources defined!

  6. Now we’re creating a new dynamic property for the key search.url and we’re adding a callback that prints the new value of this property when it has changed

  7. We’re now printing the current value of this property which should be http://www.test.de

  8. Then we’re updating the value in our ZooKeeper instance using the test client and the callback should print that value (http://www.foo.de)

package com.hascode.tutorial;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.retry.RetryOneTime;

import com.google.common.io.Files;
import com.netflix.config.ConcurrentCompositeConfiguration;
import com.netflix.config.ConcurrentMapConfiguration;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;
import com.netflix.config.DynamicWatchedConfiguration;
import com.netflix.config.source.ZooKeeperConfigurationSource;
import com.netflix.curator.test.TestingServer;

public class App {

	public static void main(String[] args) throws Exception {
		try (TestingServer zkTestServer = new TestingServer(2181, Files.createTempDir());
				CuratorFramework testClient = CuratorFrameworkFactory.newClient(zkTestServer.getConnectString(),
						new RetryOneTime(2000))) {
			testClient.start();
			testClient.create().forPath("/config");
			testClient.create().forPath("/config/search.url", "http://www.test.de".getBytes());

			// enable JMX dynamic configuration
			System.setProperty(DynamicPropertyFactory.ENABLE_JMX, "true");

			// enable config from properties file
			ConcurrentMapConfiguration configFile = new ConcurrentMapConfiguration(
					new PropertiesConfiguration("config.properties"));

			// enable configuration from system properties
			ConcurrentMapConfiguration systemProps = new ConcurrentMapConfiguration(new SystemConfiguration());

			CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181",
					new ExponentialBackoffRetry(1000, 3));
			client.start();

			ZooKeeperConfigurationSource zkConfigSource = new ZooKeeperConfigurationSource(client, "/config");
			zkConfigSource.start();
			System.out.println("value in zookeeper: " + zkConfigSource.getCurrentData().get("search.url"));
			DynamicWatchedConfiguration zkDynamicConfig = new DynamicWatchedConfiguration(zkConfigSource);

			// composite configuration
			ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
			config.addConfiguration(zkDynamicConfig);
			config.addConfiguration(configFile);
			config.addConfiguration(systemProps);
			ConfigurationManager.install(config);

			DynamicStringProperty prop = DynamicPropertyFactory.getInstance().getStringProperty("search.url",
					"https://www.google.com/#q=");

			prop.addCallback(() -> System.out.println("search url has changed to " + prop.get()));

			System.out.println("search url is: " + prop.get());

			testClient.setData().forPath("/config/search.url", "http://www.foo.de".getBytes());

			System.console().readLine("press enter to shutdown application\n");
			System.out.println("shutting down application..");

			client.close();
		}
	}

}

Running the Example

We’re now ready to run our example application in our IDE of choice or using Maven in the command line like this:

$ mvn exec:java -Dexec.mainClass=com.hascode.tutorial.App
[INFO] ------------------------------------------------------------------------
[INFO] Building netflix-archaius-tutorial 1.0.0
[INFO] ------------------------------------------------------------------------
value in zookeeper: http://www.test.de
search url is: http://www.test.de
press enter to shutdown application
search url has changed to http://www.foo.de

shutting down application..

To update the configuration value we may simply use JConsole, connect to our process and update the value as show on the following screenshot:

archaious jmx properties 1024x751
Figure 2. Updating Archaius settings with JMX and JConsole

Tutorial Sources

Please feel free to download the tutorial sources from my GitHub repository, fork it there or clone it using Git:

git clone https://github.com/hascode/netflix-archaius-tutorial.git

Alternative: Java Config-Builder

I have written about another configuration library in my tutorial “Using Java Config-Builder to assemble your Application Configuration“, please feel free to read.

Netflix Hystrix

If interested in using Netflix Hystrix, please feel free to read the following tutorial of mine: "Resilient Architecture in Practice – Circuit Breakers for Java: Failsafe, Javaslang, Hystrix and Vert.x".

Article Updates

  • 2017-02-14: Link to circuit-breaker article added.