Search code examples
javadesign-patternsakkatypesafe-config

Loading and sharing configuration data in an Akka project


I’ve defined two application.conf files, one for each environment, namely dev and prod :

application-dev.conf
application-prod.conf

To read the configuration values I use a config object builder (using lombok) named ConfigurationObject to set the values read from the application configuration files. The configuration object just stores two properties ‘kafkaBrokers’ and ‘someOtherConfig’ :

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ConfigLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConfigLoader.class);

    public static void main(String[] args) {

        String env = "dev";
        final Config configDev = ConfigFactory.load("application-"+env+".conf");
        final ConfigurationObject configurationObjectDev = ConfigurationObject.builder()
                .kafkaBrokers(configDev.getString("kafka-config.brokers"))
                .someOtherConfig(configDev.getString("kafka-config.brokers"))
                .build();

        env = "prod";
        final Config configProd = ConfigFactory.load("application-"+env+".conf");
        final ConfigurationObject configurationObjectProd = ConfigurationObject.builder()
                .kafkaBrokers(configProd.getString("kafka-config.brokers"))
                .someOtherConfig(configProd.getString("kafka-config.brokers"))
                .build();

        LOGGER.info(configurationObjectDev.toString());
        LOGGER.info(configurationObjectProd.toString());

    }
}

Here I’ve defined the configuration object to store the values read from application.conf :

import lombok.Builder;
import lombok.ToString;

@Builder
@ToString
public class ConfigurationObject {
    final String kafkaBrokers;
    final String someOtherConfig;
}

Should I just use ConfigFactory.load directly instead of wrapping each property from a given environment application.conf to a configuration object, in this case ConfigLoader. Is there a pattern I should use for this ?

Application conf files :

application-dev.conf :

kafka-config {
    brokers="broker1,broker4"
    brokers = ${?MY_KEY_ENV}
}

some-other-config {
    brokers="broker-prod1,broker-prod4"
    brokers = ${?MY_KEY_ENV}
}

application-prod.conf :

kafka-config {
    brokers="broker-prod1,broker-prod4"
}
some-other-config {
    brokers="broker-prod1,broker-prod4"
   brokers = ${?MY_KEY_ENV}
}

Solution

  • It's definitely not an uncommon pattern in Lightbend Config/HOCON to define a class representing your application's configuration. On the Scala side, there are tools like PureConfig which make it easy to read a config into such a class. I'm not aware of similar tools on the Java side (e.g. which reflectively look at the configuration class and let you build an instance of that class from the Config object, inferring the schema along the way), but I don't think such tools are impossible.

    Whether or not you're defining such a class, it's generally a good idea to have one and only one load of config in an application and then pass either the class you defined to represent the configuration or the Config object around: file/resource I/O and parsing is not free and multiple loads can add cognitive load.

    I'm a little dubious on reading both application-dev.conf and application-prod.conf. If your application needs both sets of Kafka brokers (e.g. it's consuming from one and producing to the other), then it might be worth having (e.g.) a source-kafka and destination-kafka config stanza in a single config file. Alternatively, if your application will only in practice be using one config, you can specify the config.file property (e.g. by adding -Dconfig.file=application-dev.conf on the Java command line) and then just use ConfigFactory.load(). In such cases, it may be a good idea to combine config files via HOCON's file inclusion capability. This is often used to define an overrides config file with deployment-/environment-specific config which includes the regular application.conf which combines more universal settings.