The Apache Onami-Configuration library is a small Google Guice extension that allows expanding com.google.inject.name.Names variables. That means that users can define properties with placeholders and let Guice resolve them to be injected with replaced values.
Users have just to invoke the org.apache.onami.configuration.OnamiVariablesExpander.expandVariables method grouping the Modules where properties have been bound, like in the sample below:
import static org.apache.onami.configuration.OnamiVariablesExpander.expandVariables; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.name.Names; ... Injector injector = Guice.createInjector( expandVariables( new AbstractModule() { @Override protected void configure() { Properties properties = new Properties(); properties.put( "JDBC.host", "localhost" ); properties.put( "JDBC.port", "3306" ); properties.put( "JDBC.schema", "test" ); ... Names.bindProperties( binder(), properties ); } }, new AbstractModule() { @Override protected void configure() { bindConstant() .annotatedWith( Names.named( "JDBC.url" ) ) .to( "jdbc:derby://${JDBC.host}:${JDBC.port}/${JDBC.schema}" ); } } ... ) );
That's all! When requesting the injection of JDBC.url, properties will be correctly expanded:
public final class JdbcConfiguration { @Inject @Named( "JDBC.url" ) private String jdbcUrl; // jdbc:derby://localhost:3306/test ... }
Apache Onami-Configuration supports the well known ${} expression to define placeholders, which scope is the whole configuration, that means that users can define some commons properties in one properties file:
commons.host=localhost commons.port=8080 ...
then referencing them in different files:
ldap.host=${commons.host} ldap.port=${commons.port} ...
In Java code users can retrieve a property from java.util.Properties specifying the default value if the input key is not present, using the Properties#getProperty(java.lang.String, java.lang.String) method.
Apache Onami-Configuration allows doing the same, users can specify a key using the pipe character | to separate the key name from the default value:
JDBC.url=jdbc:derby://${JDBC.host|localhost}:${JDBC.port|1527}/${JDBC.schema}
that means if JDBC.host and JDBC.port won't be specified, they will be replaced by the default values; given the following properties:
JDBC.url=jdbc:derby://${JDBC.host|localhost}:${JDBC.port|1527}/${JDBC.schema} JDBC.schema=rocoto
the JDBC.url property value will be jdbc:derby://localhost:1527/rocoto.
property=${not.found|${not.found.again|Crap!}} # property = Crap! not.found.again=Fallback! # property = Fallback!
hello.en=Hi! hello.fr=Salut ! hello.i18n=${hello.${locale|en}} # hello.i18n = Hi! locale=fr # hello.i18n = Salut!
# Will not thow a stack overflow GNU=${GNU}'s Not UNIX
g=2g gg2=2 h=${${${g}4|${g}2}} 2g=gg 2g2=4 gg=${h}${4|${${2g}2}} # gg=42
I had a not so good experience with the Spring 2 PropertyPlaceholderConfigurer because it forces users to centralize the properties files loading; I mean, to make things working, in the way that ${} expressions were correctly resolved, I had to declare just one PropertyPlaceholderConfigurer for the whole context, list all the *.properties files for the all modules.
I didn't like that approach at all, it doesn't allow users to split configurations in different modules even if some keys are shared by two or more modules. Apache Onami-Configuration users won't have these problems at all, they can load *.properties files from different locations and obtain the same result.