Fork me on GitHub

Importing properties

You can use another mechanism to load your properties into a mapping interface. And this mechanism is to specify a Properties object programmatically when calling ConfigFactory.create():

public interface ImportConfig extends Config {

    @DefaultValue("apple")
    String foo();

    @DefaultValue("pear")
    String bar();

    @DefaultValue("orange")
    String baz();

}

// then...

Properties props = new Properties();
props.setProperty("foo", "pineapple");
props.setProperty("bar", "lime");

ImportConfig cfg = ConfigFactory
    .create(ImportConfig.class, props); // props imported!

assertEquals("pineapple", cfg.foo());
assertEquals("lime", cfg.bar());
assertEquals("orange", cfg.baz());
Null keys and Null values are invalid.

A Properties or Map object accepts null key or null values, but that is obviously an error, so starting from version 1.0.10, an IllegalArgumentException is thrown.

The exception message also contains further information about the offending key, if applicable.

You can specify multiple properties to import on the same line:

ImportConfig cfg = ConfigFactory
    .create(ImportConfig.class, props1, props2, ...);

If there are prop1 and prop2 defining two different values for the same property key, the one specified first will prevail:

Properties p1 = new Properties();
p1.setProperty("foo", "pineapple");
p1.setProperty("bar", "lime");

Properties p2 = new Properties();
p2.setProperty("bar", "grapefruit");
p2.setProperty("baz", "blackberry");


ImportConfig cfg = ConfigFactory
    .create(ImportConfig.class, p1, p2); // props imported!

assertEquals("pineapple", cfg.foo());

// p1 prevails, so this is lime and not grapefruit
assertEquals("lime", cfg.bar()); 

assertEquals("blackberry", cfg.baz());

This is pretty handy if you want to load properties provided by other mechanisms which not accessible through any of the supported URI schemes listed under Loading Strategies

For instance, a Java EE (a.k.a. Jakarta EE) servlet running on a servlet container might load properties during initialization from a resource accessible through its respective ServletContext:

interface ServletContextProperties extends Config {
    /** JDBC name of a data source used by the servlet */
    @Key("ds.name")
    String dsName();

    void list(PrintStream out);
}

...

public class MyServlet extends HttpServlet {

    protected void init() {
        ServletContextProperties cfg = ConfigFactory
            .create(ServletContextProperties.class, 
                    getServletConfig().getServletContext().getResourceAsStream("/WEB-INF/myServlet.properties"));
    }
</div>
}
Note that this way of proceeding yields the responsibility of proper usage to the client, whose code shall never 'forget' to include the import parameter when calling the ConfigFactory.create() ).

Thus, if you want to refer to properties provided by any of the mechanisms directly supported by the @Source annotation, you should rather use them, as explained in Loading strategies. In particular, to refer to system properties or environment variables, you can use (since version 1.0.10) system:properties or system:env (respectively).

Other typical usage of importing properties might involve loading them from other sources directly provided by the execution environment, e.g. servlet context attributes, context or servlet initialization parameters, JNDI application environment resources (i.e. entries under java:comp/env/), Java preferences, or any other environment-dependent property sources. However, none of these sources direcly provide an API to access their contents as a Map object; hence the programmer would need in that case to implement first their own method to convert from lists of names plus individual values to Map object (therefore compatible with Owner API). </div>

Interactions with loading strategies

Notice that the “importing properties” feature is additional to the properties loading mechanism explained in chapter Loading strategies.

Properties imported programmatically have higher priority regarding the properties loaded from the @Sources attribute.

Imagine the scenario where the you define your configuration with @Sources annotation, but you want to allow the user to specify a configuration file at the command line.


@Sources(...)
interface MyConfig extends Config { 
    ...
}

public static void main(String[] args) {
    MyConfig cfg;
    if (args.lenght() > 0) {
        Properties props = new Properties();
        props.load(new FileInputStream(new File(args[0])));
        cfg = ConfigFactory.create(MyConfig.class, userProps);
    } else {
        cfg = ConfigFactory.create(MyConfig.class);
    }
}

In the above example, the properties file specified by the user will override the properties loaded by @Sources if there is overlapping between the properties names. This approach is used by many command line tools, that allow the user to specify a configuration on the command line that overrides the default one.

This is true only with version 1.0.3.1 and superior!

Be aware that in versions prior to 1.0.3.1 imported properties have lower priority than others loaded properties. This behavior has been changed in version 1.0.3.1 and it will be kept this way for future releases.