Wiki

Clone wiki

XNConfig / Home

Developers usually need to maintain multiple configurations for a Java project . For instance, a project may have a development configuration and a production configuration. It is a boring and error-prone process to maintain and deploy these different configurations. Spring 3.1 tries to solve this problem with the support of Environment. Unfortunately, this solution is not ideal for two reasons:

  1. XML is not necessarily a desirable format to hold configurations;
  2. This solution still requires you to maintain multiple configuration files;

XNConfig is an easy solution that makes configuration maintenance much simpler. The syntax of XNConfig is straightforward, making it a breeze to use it in your project; even better, with XNConfig, developers only need to maintain one single configuration file, which can be deployed to various environments.

Example

Here is a simple example to demonstrate how XNConfig work. Let's suppose you have a service application that requires database access. After talking to you DBA, you know that:

  1. in the development environment, the application uses parameters below to connect to the database:
    • JDBC Url: jdbc:mysql:dev:3306/schema
    • User name: dev
    • Password: dev_password
    • Max connection pool size: 20
  2. In the production environment, you need to use these paramaters:
    • JDBC Url: jdbc:mysql:prod:3306/schema
    • User name: prod
    • Password: prod_password
    • Max connection pool size: 20

Here is the single XNConfig file that defines those database configurations for your application:

  /* comment */  

  *.db.url = "jdbc:mysql://dev:3306/schema";       # ';' to delimiter configuration entries

  *.db.username = "dev";

  *.db.password = "dev_password";

  *.db.max_connection = 20; # beside string, boolean and long/integer values are also supported

  

  # this is also a comment

  prod.db.url = "jdbc:mysql://dev:3306/schema";

  prod.db.username = "dev";

  prod.db.password = "dev_password";

  prod.db.max_connection = 20;      

Let's check the two configuration entires for "db.url":

  1. "db.url" is the name of the configuration entry;
  2. Wildcard prefix "*" means default. "*.db.url" defines default value for "db.url" is "jdbc:mysql:dev:3306/schema";
  3. Prefix "prod" means an environment named "prod". Entry "prod.db.url" reads the value of "db.url" in "prod" environment is "jdbc:mysql:dev:3306/schema";
  4. When XNConfig looks value for "db.url" in an environment named "dev", because "dev.db.url" cannot be found in the configuration file, XNConfig returns the default value "jdbc:mysql:dev:3306/schema";

Fairly simple, is it?

Prefix

Here are a few more words on the "Prefix". By default, XNConfig assumes the number of prefix is 1. That means you can have prefixes like "*." or "prefix_2.". If you provide prefix "*.*", XNConfig will throw exception; if the prefix provided is "p1.p2", XNConfig thinks "p2" is part of configuration entry name.

In order to define 2 or more number of prefixes, adding PREFIX_NUM configuration at the beginning of your configuration file:

# change the prefix number to 2

! PREFIX_NUM = 2;

*.*.db.url = "jdbc:mysql://dev:3306/schema"; # "*.*" is the prefix; "db.url" is the entry name

prod.myapplication.db.url = "jdbc:mysql://dev:3306/schema"; # "prod.myapplication" is the prefix

At last, all wildcards of a prefix must be at the beginning. For example, "\*.prod.db.url" is allowed; but "prod.\*.db.url" is not.

Code

To ask XNConfig to read proper configurations for your code, you must do two things:

  1. Tell XNConfig which configuration file to parse;
  2. Tell XNConfig what is the environment your application is running in;

To do 2, you need to implement an interface called ProfileResolver. It has only one method named "getProfile", which returns the prefix string of your application environment. The complete code to read the above database configuration file is:

    public static void main(String... argv) throws XNConfigException, IOException {
        XNConfig config = new XNConfig(XNConfig.class.getResourceAsStream("database.properties"));
        config.setProfileResolver(new ProfileResolver() {
            public String getProfile() {
                return "dev";
            }
        });

        System.out.println(config.getValue("db.url"));  // print out "jdbc:mysql://dev:3306/schema
    }

XNConfig uses ANTLR (http://www.antlr.org/) to parse configuration files. To run the code above, it is needed that you have ANTLR (version 3.4) in your classpath.

Maps and List

You can also use Maps and Lists in the XNConfig file. The syntax to define a map or a list is very similar to JSON:

*.db.connection_options = {

	auto_commit:false,                         #',' to separate items in a map or in a list

	idleConnectionTestPeriod: 100

};

*.authorized_role = ["admin", "user", "guest"];

prod.authorized_role = ["user", "guest"];

It is also possible to embed one collection into another to create fairly complicated configurations. For example:

*.outer_map = {

	list_in_map: [1, 2, 3, 4, 5],

	internal_map: {

		deeper_list: ["a", "b", "c"],

		end: true

	}

};

References

To make writing the configuration file even easier, XNConfig allows developers to make reference to configuration entries. You can use this feature to make the configuration eye-friendly:

# this configuration defines the same outer_map as the above

*.list_1 = [1, 2, 3, 4, 5];

*.list_2 = ["a", "b", "c"];

*.internal_map = {

	deeper_list: @list_2, # '@' means referring. Note that there is no '*.' before 'list_2'

	end:true

};


*.outer_map = {

	list_in_map: @list_1,

	internal_map: @internal_map

};

If a reference cannot be resolved, XNConfig assumes its value is null. XNConfig cannot detect circular references unless one is actually being used. Upon then, a runtime exception will be thrown.

Spring Integration

You can import XNConfig configuration entries into Spring as spring beans (repository: https://bitbucket.org/xboxng/xnconfigspring). Here is an example. Assume you have a property file as below:

! PREFIX_NUM = 2;
*.*.website = "http://www.xboxng.com";

*.*.password = "hello_world";
*.*.url = @website;

*.*.options = {
    log_exceptions: false,
    max_try_number: 10,
    website: @website,
    roles: ["guest", "user"]
};

To import all these configuration entries into Spring, in your Spring configuration file, add a bean of type *com.xboxng.config.spring.XNConfigFactoryBean*:

<bean id="config" class="com.xboxng.config.spring.XNConfigFactoryBean">
	<property name="configFile" value="classpath:test.properties"></property>
        <!-- Instead of hardcoding "dev.config", in your application, -->
        <!-- you should read profile during runtime from, for example, your system properties, -->
        <!-- or you can inject a profileResolver to the profileResolver property of this bean. -->         
	<property name="profile" value="dev.config" />
</bean>

That's it! After that, you can refer string beans "website", "url", "password" and map "options" as regular Spring beans.

To avoid bean name collision, you may want to add a bean name prefix to all XNConfig beans by setting the beanPrefix property of XNConfigFactoryBean to something like "xnconfig/". If this property is set, you need to, for example, access "website" bean with name "xnconfig/website".

If you have many configurations, and only wanna use few of them into Spring, set the configEntries of XNConfigFactoryBean to the list of entries interest you. For example, following sample tells XNConfig to only import website and password configuration entries.

<property name="configEntries">
	<list>
		<value>website</value>
		<value>password</value>
	</list>
</property>

The unit test of XNConfigSpring is a good example to follow if you wanna use XNConfig with Spring together.

License

XNConfig is licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)

Source code

How to build the library

Install maven, and run the following command under folder XNConfig

$ mvn clean package

and look for the generated XNConfig-1.0.jar file. Do the same under folder XNConfigSpring to create library XNConfigSpring-1.0.jar.

Updated