How to get a configuration in Vertx apps with Java

Hi again! I want to talk today about Vertx-Config library, that is the prefered way to get configuration in Vertx apps. It offers abstraction layer for various type of configs – properties files, system vars; as well some more exotic stores, that are available via extensions – Vault, Kubernetes and others. In this post we would observe Vertx Configuration model, and take a look on built-in configuration providers. Later, we would see how to track configuration updates during runtime.

Vertx Configuration components

Vertx configuration model is a programming abstraction, which is implemented by Vertx-config library. There are two main components, that we use in order to read configuration in Vert.x apps: ConfigProvider and ConfigStore. Both are interfaces that define abstractions. ConfigProvider reads configuration and tracks for updates. ConfigStore abstracts type and format of configuration, for instance it can be JSON, key-value or Yaml format (requires extension) and can be placed in file system, system variables or in remote source. We also use options: ConfigStoreOptions and ConfigRetrieverOptions that hold data structures, we pass into the retriever or the config store.

Basically, the flow of getting configuration is pretty straightforward:

  1. Define ConfigStore
  2. Pass ConfigStore to ConfigRetrieverOptions
  3. Create ConfigRetriever with specified ConfigRetrieverOptions

Later in this post, we would also see, how to track configuration updates. For starting, let learn how to get a configuration on startup.

Do you want to learn more about Vert.x framework and Java microservices? Take a look on the complete list of my Vertx tutorials and case studies.

Set a up a demo project

We would use Vertx-starter to bootstrap our demo project. Vertx starter already includes Core library, so we just need to add Vertx Config library to our project. Download a boilerplate project and import to your IDE (I use Maven in this post):

Let do some coding now. We need to define Verticle, that would read configuration. By the way, if you are not familiar with Vert.x framework, I encourage you to take a look on my free Vertx microservices tutorial. Functionality of verticle is dummy: we define configuration flow and then print what we got:

ConfigReaderVerticle.java

public class ConfigReaderVerticle extends AbstractVerticle {
	
	private ConfigRetrieverOptions retrieverOptions;

	public ConfigReaderVerticle(IConfigSource configSource) {
		this.retrieverOptions = new ConfigRetrieverOptions()
				.addStore(configSource.getConfigStore());
	}
	
	@Override
	public void start(Promise<Void> startPromise) throws Exception {
		ConfigRetriever retriever = ConfigRetriever.create(vertx, retrieverOptions);
		retriever.getConfig(res->{
			if (res.succeeded()) {
				JsonObject config = res.result();
				System.out.println(config);
				startPromise.complete();
			} else {
				startPromise.fail("Cannot get configuration");
			}
		});
	}
}

Application.java

public class Application {

	public static void main(String[] args) {
		Vertx vertx = Vertx.vertx();
        // here create configsource
		vertx.deployVerticle(new ConfigReaderVerticle());
	}

}

Now, we are able to read configuration. Pay attention, that initially we get config, when Verticle is started. Later in this post, we would add tracking of configuration changes. Also, note that config is nothing that familiar to us traditional JsonObject. Next, let define an interface IConfigSource. We would use it in order to abstract getting config store options. It has an incredibly easy structure:

public interface IConfigSource{

    ConfigStoreOptions getConfigSource();

}

Read configuration on startup

In this section we would see how to read various types of configurations, that are available out of the box. Vert.x offers us following options:

  1. Properties files
  2. Environment variables
  3. System variables
  4. Eventbus
  5. Remote http configuration

We would explore most important ones. Data could be defined in JSON format or as key-value pairs. If you need additional types of data, like yaml or additional stores, like Vault – consider to check out documentation, because Vert.x offers additional extensions for this purposes.

Reading properties

This is one of the oldest way to pass configuration to Java apps. Usually, we use Properties class to read config, but Vert.x-Config abstracts this way.

1. Create Properties file

We can’t read properties from file, if we don’t have one. Create a dummy properties file and place it to resources folder:

2. Create ConfigStore

Next step is to define ConfigStoreOptions. These options are basically beans where we pass type, format of store and config – everything specific to store (that are JSONs). We use ConfigSources to abstract the process, but it is not required. For properties files, we need following params:

public class FileConfigSourceImpl implements IConfigSource {

	@Override
	public ConfigStoreOptions getConfigStore() {
		ConfigStoreOptions options = new ConfigStoreOptions();
        options.setType("file");
        options.setFormat("properties");
        options.setConfig(new JsonObject().put("path", "conf.properties"));
        return options;
	}

}

NB It is assumed that properties file is placed to the resource folder, by default Vertx starter does not include it. You need to create it yourself. While the process is easy in IDEA, in Eclipse things are more complicated. If you are new to Eclipse (I am also quite new to this IDE), don’t be shy to check this Stackoverflow question.

3. Read configuration

Finally, we read configuration in our app by passing the respected config source to the verticle’s instance:

Application.java

IConfigSource configSource = new FileConfigSourceImpl();
vertx.deployVerticle(new ConfigReaderVerticle(configSource));

Now, run our example and check results:

{"format":"properties","location":"local","type":"file"}

Reading environment variables

Environment variables – key/value pairs – are an another way to pass configuration to Java applications. Usually, it is done via static getenv method, where we pass a name of the desired variable (e.g. key) as an argument and get variable (e.g. value). Again, Vertx config abstracts this as a specific config store. Let have a look.

1. Set variables

Ways to set environment variables depend on the underlaying system. In this post, I use built-in tools of Eclipse to do it (note, that steps can be different in your IDE).

2. Create ConfigStore

As in the previous example, we utilize IConfigSource abstraction again, but it is not required. You need to define following ConfigStoreOptions:

public class EnvVarsStoreSource inplements IConfigSource{

    @Override
    public ConfigStoreOptions getConfigSource(){
        ConfigStoreOptions options = new ConfigStoreOptions();
        options.setType("env");
        return options;
    }
}

Keep in mind, that the environment variables configuration store does not support the format field. By default, the retrieved value is transformed into JSON compatible structures, like numbers, strings, booleans, JSON objects and JSON arrays (reference). What we can do, if we want to avoid such behavior, we can pass JsonConfig with raw-data:true. If raw-data is true no attempts to convert values is made, and you’ll be able to get raw values using config.getString(key). It is useful when manipulating large integers:

public class EnvVarsStoreSource inplements IConfigSource{

    @Override
    public ConfigStoreOptions getConfigSource(){
        ConfigStoreOptions options = new ConfigStoreOptions();
        options.setType("env");

        // to orevent convertions:
        options.setConfig(new JsonObject().put("raw-data", true));

        return options;
    }
}

3. Read configuration

Now, we can read environment variables. Run a project and assert results:

{..., "format":"keyvalue","location":"local","type":"env", ...}

NB this provider prints all environment variables, not only as defined – full stdout is skipped.

Reading remote JSON config

Finally, let see how to read a configuration that is stored as JSON-encoded data on a remote server. According, to docs, the HTTP configuration store retrieves the configuration from an HTTP location. It can use any supported format – we would use JSON.

1. Set remote JSON

Let create some dummy JSON data first. Create new JSON file with following data:

{"format":"json","location":"remote","type":"file"}

Next, we need to deploy it on a remote location. Note, this configuration store initialises an underlaying Vert.x HTTP client in order to access remote file.

2. Create ConfigStore

Same as before, we use IConfigSource abstraction to get config store options instance. Take a look on a code snippet below:

public class RemoteConfigSourceImpl inplements IConfigSource{

    @Override
    public ConfigStoreOptions getConfigSource(){
        ConfigStoreOptions options = new ConfigStoreOptions();
        options.setType("http")
        options.setConfig(new JsonObject()
            .put("defaultHost", "https://mednikov.net")
            .put("path", "/test/config.json"));
        return options;
    }
}

3. Read configuration

And as a final step, we can run our app and assert that everything works as expected:

{"format":"json","location":"remote","type":"file"}

Listen configuration updates

The second problem, that I want to address in this post, is how to listen config updates. Until now, we received a configuration during startup of our application. But Vert.x also permits us to track config updates. The ConfigRetriever with a specified interval retrieve the configuration, and if the outcome is different from the current one, our application can be reconfigured. The default interval is 5 seconds, it can be overriden. How to do this?

  1. Create ConfigRetrieverOptions, where to specifiy update options, for example the aforesaid update interval.
  2. Create ConfigRetriever
  3. Set retriever update listener. This listener is a handler, that uses ConfigChange object, that is a structure representing a configuration change.

Let have an example. Take a look on the code snippet below:

public class ConfigReaderVerticle extends AbstractVerticle {
	
	private ConfigRetrieverOptions retrieverOptions;

	public ConfigReaderVerticle(IConfigSource configSource) {
		this.retrieverOptions = new ConfigRetrieverOptions()
				.addStore(configSource.getConfigStore())
                // 1. add scan period - in mills
                .setScanPeriod(3000);
	}
	
	@Override
	public void start(Promise<Void> startPromise) throws Exception {
		ConfigRetriever retriever = ConfigRetriever.create(vertx, retrieverOptions);
		
		 // 2. set change listener
		retriever.listen(change->{
			JsonObject newConfig = change.getNewConfiguration();
			System.out.println("Configuration is changed:");
			System.out.println(newConfig);
		});

		retriever.getConfig(res->{
			if (res.succeeded()) {
				JsonObject config = res.result();
				System.out.println("Initial config:");
				System.out.println(config);
				startPromise.complete();
			} else {
				startPromise.fail("Cannot get configuration");
			}
		});
	}
}

Run and check results:

Initial config:
{"format":"properties","location":"local","type":"file"}

Insert some update a properties file and save:

Initial config:
{"format":"properties","location":"local","type":"file"}
Configuration is changed:
{"format":"properties","location":"local","type":"file", "status":"updated", "message":"hello"}

Do you want to learn more about Vert.x framework and Java microservices? Take a look on the complete list of my Vertx tutorials and case studies.

Conclusion

As you could see in this post, Vertx-Config library is a powerful instrument to get configuration for Vertx apps. Advantages of this library are numerous: a single easy-to-use abstraction for various config stores, a great number of supported providers (both built-in and with extensions), ability to monitor changes and propagate config via EventBus. In this post we’ve observed how to start with Vertx-Config library, e.g. how to get configuration from built-in config stores and how to listen config updates. Also, we’ve described the Vertx Config model.

I encourage you to follow my twitter andreevi_ch and linkedin to be informed about my new posts. If you have questions, write me message or drop comment below. Have a nice day!