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:
for entries that are shared across the entire [fleXive] installation.
for entries that are shared across a [fleXive] division.
for entries that are specific to an individual node in the network,
such as filesystem paths. The node ID defaults to the node's
hostname, but can be overridden with the system property
flexive.nodename
.
The current node name can be determined using
NodeConfigurationEngine#getNodeName().
for entries that are shared across a [fleXive] application. The application is identified using FxContext#getApplicationId() , which is initialized with the servlet context path (for web applications). Thus, if the context path changes, the existing application-based configuration entries will no longer be available.
The application scope was introduced in [fleXive] 3.1.
for entries that can be customized by the user.
A ParameterScope also specifies a fallback scope, for example a user parameter uses application as its fallback scope: the value stored in the application configuration acts as a default value for all users, who can (but do not have to) override this with their own setting.
Fallback scopes are applied recursively, so a parameter that uses application as a fallback scope will also use the fallback scopes of application, and so on. The global configuration is the exception to this rule, because it will never be used as a fallback: the global configuration is (by definition) outside the scope of a running [fleXive] application, and cannot be updated without using the authentication methods of the GlobalConfigurationEngine .
A parameter is defined using one of the ParameterFactory 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 has user scope, which (by definition) uses the application configuration as a fallback. This means we can now store values both in the application and user configuration (and because the fallback scopes of application are also used, also in the division configuration). Of course, unless we are logged in as a global supervisor, we cannot update the application 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));