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}
}
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
Configobject, 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
Configobject 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.confandapplication-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.) asource-kafkaanddestination-kafkaconfig stanza in a single config file. Alternatively, if your application will only in practice be using one config, you can specify theconfig.fileproperty (e.g. by adding-Dconfig.file=application-dev.confon the Java command line) and then just useConfigFactory.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 regularapplication.confwhich combines more universal settings.