Configuration Engine

The configuration engine provides a hierarchical storage for configuration properties. It allows to override default parameters with user-specific settings, and uses the distributed cache to avoid database lookups.

A configuration parameter is characterized by the following properties:

  • The key is a unique identifier for the actual configuration entry.

  • The path allows to organize configuration entries similar to a file system. Keys are unique (only) for the same path.

  • The scope tells the configuration engine where the configuration entry can be stored:

    global

    for entries that are shared across the entire [fleXive] installation

    division

    for entries that are shared across a [fleXive] division

    user

    for entries that can be customized by the user.

    The scope also includes fallback scopes, for example a user parameter may use division for its fallback scope: the value stored in the division configuration acts as a default value for all users, who can (but do not have to) override this with their own setting.

A parameter is defined using one of the ParameterFactory factory methods. The parameter interface uses Java generics to provide typesafe manipulation of the configuration values, for example it is not possible (using the configuration APIs) to store a String value in an Integer parameter. Let's start with declaring a configuration parameter of type Integer:

public static final Parameter<Integer> INT_PARAM =
    ParameterFactory.newInstance(
        Integer.class,       /* value class */
        "/config/test",      /* path */
        ParameterScope.USER,
        "param1"             /* key */,
        21                   /* default value */
    );

Our new parameter uses user scope, which (by definition) uses the division configuration as a fallback. This means we can now store values both in the division and user configuration. Of course, unless we are logged in as a global supervisor, we cannot update the division configuration, but any user can update his or her own configuration. By default, the configuration engine uses the "least shared" scope, in our case the user scope. The following call puts a value in the configuration of the calling user and displays the new value:

EJBLookup.getConfigurationEngine().put(INT_PARAM, 123456);

System.out.println("User parameter value: " + EJBLookup.getConfigurationEngine().get(INT_PARAM));

Having global supervisor privileges (e.g. in a run-once script) we can also update the fallback value using the division configuration engine (which has the same interface as the configuration engine, but always uses division scope):

EJBLookup.getDivisionConfigurationEngine().put(INT_PARAM, 123456);

The configuration engine supports generic value types through serialization to XML with XStream. The ParameterFactory methods return optimized parameter implementations for most primitive values (e.g. Integer, Long or String), and uses the XStream-based implementation for everything else. For example, the following code declares and uses a parameter for values of type java.awt.Point:

// declare parameter
final Parameter<Point> POINT_PARAM = ParameterFactory.newInstance(Point.class,
                            "/config/test",         /* path */
                            ParameterScope.USER,
                            "pointParam",           /* key */
                            new Point(10, 20)       /* default value */);

// store Point value
EJBLookup.getConfigurationEngine().put(POINT_PARAM, new Point(3, 4));

// retrieve stored value
System.out.println("Point parameter: " + EJBLookup.getConfigurationEngine().get(POINT_PARAM));