Tuesday, April 15, 2014

Configuration Formats and Locations

Configuration Formats and Locations

Configuration Formats

Configuration data can be stored in various formats. With the JDK a few possible formats are included by default:

  • .properties files, readable with java.util.Properties enable storing simple key, value pairs in an ISO-8859-1 encoded text file, also supporting Unicode escapes.
  • The same java.util.Properties class also provides a corresponding .xml formatted variant, which benefit from all the xml encoding options.
  • Parameters passed with -Dkey=value on the Java command line are accessible from System.getProperties().
  • Finally environment properties inherited by the underlying runtime platform are accessible from System.getenv().
All this mechanisms are provided by the Java SE platform out of the box and therefore are widely used. But there are for sure more possible formats that might be used as source of configuration, e.g. other xml formats, JSON or databases. Therefore it makes sense to model the configuration format explicitly, so custom (or legacy) formats can be supported easily:


public interface ConfigurationFormat{
    String getFormatName();
    boolean isAccepted(URI resource);
    Map<String,String> readConfiguration(URI resource);
}

Implementations of this class can be simply registered using different component loading mechanism, such as java.util.ServiceLoader or, in case of Java EE, alternately as CDI managed bean. Access to the formats can be obtained by a corresponding singleton, which provides 
  • access to common formats, such as property, or xml-property files.
  • access to other (registered) formats by name
  • access to all currently registered format names
  • access to a matching format given an URI of a resource.

public final class ConfigFormats{

    private ConfigFormats(){}

    public static ConfigurationFormat getFormat(                                          String formatName);
    public static Collection<String> getFormatNames();
    public static ConfigurationFormat getFormat(URI resource);
    public static ConfigurationFormat getPropertiesFormat();
    public static ConfigurationFormat getXmlPropertiesFormat();

}

Also the singleton accessor for accessing predefined maps can be easily enriched by corresponding methods (though with increasing complexity and similar method signatures building a Builder maybe more appropriate):

public final class PropertyMaps{

    private PropertyMaps(){ }

    // factory methods
    ...
    public static PropertyMap fromPaths(
              Map<String,String> metaInfo,
              ConfigurationFormat format, String... paths);
    public static PropertyMap fromPaths(ConfigurationFormat format, 
              String... paths);
}


Configuration Locations

Similar to the fact that configuration data can be formatted differently, configuration can be also be read/accessed from different locations:
  • as class path resources
  • as files on the locale file system
  • as resources accessible from a web server (or configuration server)
  • as remote data accessible from a configuration bean (EJB, managed bean, ...)
  • ...
In the above examples/API we simply pass a literal path to locate/define a configuration. Hereby the idea is that the path is formatted in a way, so multiple location mechanisms (called readers) can be transparently added/registered to the configuration system. A configuration resource then can be defined as <reader>:<location> (the ones, who know Spring will possibly see some similarities with Spring's Resource API). Examples of valid configuration resources can be:
  • classpath:cfg/test-*.xml
  • classpath*:cfg/${STAGE}/*.xml
  • file:${APP_DIR}/cfg/envconfig/*.xml
  • url:http://myconfigserver.intra.net/config/${STAGE}/get?appID=MyApp
  • ds:[ConfigDS]SELECT a.key, a.value FROM Config a WHERE a.appID="MyApp"
Hereby 
  • classpath uses ClassLoader.getResource(String), also supporting Ant-like path expressions
  • classpath* uses ClassLoader.getResources(String), also supporting Ant-like path expressions
  • file locates files on the local file system, also supporting Ant-like path expressions
  • url uses new URL(String), in the example above calling a Restful service
  • ds accesses configuration data using an OQL query, reading from the ConfigDS datasource.

The exact syntax for path expressions, of course, can be discussed and improved. Dynamic parts basically can be implemented using expression language (EL) extensions.

A ConfigurationReader hereby can be modeled by a simple interface as illustrated below:

public interface ConfigurationReader{
    String getReaderName();
    Map<String,String> readConfiguration(String readerPath);
}

Similarly to formats, readers can be managed and accessed/tested from a ConfigurationReader singleton:

public final class ConfigReaders{

    private ConfigReaders(){}
    public static ConfigurationReader getReader(                                          String readerName);
    public static Collection<String> getReaderNames();

}

So given this interfaces and accessors our configuration model now is capable of supporting more or less every type of configuration, as long as its mappable to Map<String,String>. It does not imply any constraints, how configuration must be stored and managed in an enterprise, nor does it constrain the format of the input source. 

But even with that, there are additional things that must be considered:
  • Configuration may also change. Mechanisms must be provided so configuration changes can be propagated to interested parties, both locally optionally also remotely. Such changes might also be propagated across VM boundaries e,g, by passing a serialized ChangeSet or Configuration over the network.
  • All examples as of now were programmatically defining the configuration to be used. Typically in an enterprise context this is determined by some configuration meta-model (aka meta-configuration).
  • Even worse within an application server running multiple enterprise / web applications several classloaders are active. As a consequence configuration that is provided on the classpath must be isolated along the corresponding classloader and its child class loaders.
  • Also we have not yet discussed how our configuration service can interoperate / being integrated in more detail within an EE environment. Integration hereby must be enabled on a global or domain level, e.g. for configuring administrative resources, but also interoperate with CDI, enabling powerful injection of configuration.
These are the topics for the upcoming blogs, so stay tuned!

No comments:

Post a Comment