Tuesday, April 22, 2014

Integrating SE Configuration with Java EE

Integrating SE Configuration with Java EE

A lot of people asked about integration of configuration mechanisms with Java EE, e.g. within Java EE 8. So I write down my current thinkings on this topic here, looking forward for your opinions and thinkings.


Nature of a Deployment Descriptor

Let's start analyzing quickly what the essence of a typical EE configuration file is, e.g. a web.xml deployment descriptor, a JPA persistence.xml or a CDI beans.xml , a log4j.properties or a container specific descriptor for a datasource. What all this rather different configurations have in common? Fortunately there are quite a few common aspects:

  • all configurations mentioned are files.
  • all configurations must be deployed to some well defined locations.
  • all its contents is basically of literal nature, in MIME types text/xml or text/plain.
  • all adhere to some standards or defined formats

Advantages of Literal Configuration Files

As a consequence we have some interesting options:
  • literal configuration can be placed somewhere else
  • literal configuration can be "enhanced" with some kind of resolution mechanism (e.g. using expression language)
In other words, we can place our configuration at more or less any arbitrary location, e.g.
  • we can support additional locations on the classpath
  • we can support additional file locations, e.g. as part of a domain deployment
  • we can read the configuration files from a simple web server
  • we can call some restful services to get them
  • we can manage them within a database
  • we can deploy Java classes that act as factories for providing the descriptors

Integrating with Java EE

The current Java EE stack is quite simple to benefit from a configuration mechanism, when some rather simple preconditions are met:
  • First of all backward compatibility must be ensured. So it must be possible to read configuration from the same places as before.
  • With the new configuration mechanism in place it must be defined, how additional (external) configuration is related to probably already existing configuration
    • With convention-over-configuration in mind external configuration should override configuration at the default locations, because without external configuration everything works as expected. With external configuration provided the defaults are expected to be overridden without any further flags to be set.
    • As another variant external configuration may also extend default configuration.
    • Nevertheless due to security concerns some constraints may be possible, these must be configured similarly.
Basically existing JSRs also can use a configuration service in different ways:

Integration on file or decriptor level

The most simplest way for integration is on file or decriptor level. Hereby each JSR can define and register the default locations of the deployment descriptors within the configuration service, e.g. using "meta-configuration":

  <? xml version="1.0" encoding="UTF-8" ?>
  <configuration>
    <config name="jpa.persistence-units" type="input-files">
       <location>classpath*:META-INF/persistence.xml</location>
       <override-policy>override</override-policy>
     </config>
  </configuration>



Each JSR then can use the configuration service to read the descriptors, e.g. JPA could read its persistence.xml files as follows:
  Configuration config = ...;
  Collection<InputStream> persistenceUnits =
       config.get("jpa.persistence-units", Collection.class);
  for(InputStream is:persistenceUnits){
       // read persistence unit
  }

The biggest advantage of this approach is that the existing JSRs require only a few minimal code changes to benefit from the new configuration service. Another advantage is that the configuration service does not any kind of knowledge on the internals of the configuration, it simply manages files.
Nevertheless dynamic configuration resolution may be more complicated, since the configuration service does not have any know how about the internal configuration structure. Nevertheless dynamic resolution is still possible (see later in this post).

Note: the example above uses InputStream as abstraction of a file resource. Alternate types are to be discussed here, such as String or URI.

Full Integration

Another approach would be to use the configuration API to map a JSR's configuration directly. E.g. a persistence.xml can be mapped as follows:

persistence-unit.myapp.provider=\
                        org.hibernate.ejb.HibernatePersistence
persistence-unit.myapp.jta-data-source=java:/DefaultDS
persistence-unit.myapp.properties.myprop=myPropValue
persistence-unit.myapp.properties.myprop2=myPropValue2

In this case all types of configuration mechanism can be used to provide such a configuration. Basically it would also be possible to map an existing descriptor/configuration format by providing some according conversion classes.

Dynamic Value Resolution

In many cases dynamic resolution of values simplifies things a lot (used too widely things get more complicated similarly). Also in many cases it would be sufficient to provide a basic configuration template and allow the configuration service to manage the dynamic replacements. This can be achieved, e.g. by allowing EL to be used within a configuration, e.g.

<persistence>  <persistence-unit name="myapp">    <provider>              
      org.hibernate.ejb.HibernatePersistence
    </provider>    <jta-data-source>$[config.myApp.myappDS]           </jta-data-source>
    <properties>

      ... 
    </properties>  </persistence-unit>
</persistence>
or

   persistence-unit.myapp.jta-data-source=$[config.myApp.myappDS]


Summary

Summarizing there are a couple of possible integration points. Basically all kind of EE descriptor files can be deployed at arbitrary locations, locally or remotedly, encrypted or plain or whatever is needed. With EL included dynamic resolution can be easily achieved. Finally existing JSRs can integrate with configuration services either on descriptor/file level or define a full configuration and key mapping.

Separation of concerns into a basic configuration service and how the services are integrated with Java EE, enable also a flexible collaboration and open discussion with all the other specifications. It is also important that things are discussed early, so the other specifications have time to adopt the new features. As another side effect the same configuration mechanism can similalry be used for administrative resources or alternate environments, e.g. Tomcat, Jetty etc.

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!

Tuesday, April 1, 2014

Composite Configuration

Composite Configuration

Modeling Common Aspects

Looking at Configuration my working analysis was to model it mainly as a Map<String,String> with additional meta data added. As we have seen this concept comes with several advantages:
  • The basic API ( java.util.Map) is already defined by the JDK.
  • Since keys as well as values are simple Strings, we inherit all the advantages of the final and immutable  String class, like type and thread safety.
  • since we constraint our API to this simple types, we ensure no or minimal overlaps with CDI in the EE context.
  • our model is fully compatible with Java SE, providing therefore maximal compatibility also with the SE platform.
Applied to the configuration format we would define two distinct artifacts:
  • a PropertyMap, which models the minimal requirements for a configuration map.
  • a Configuration, which extends PropertyMapand provides additional functionalities, such as extension points, type support etc.
public interface PropertyMap extends Map<String,String>{

    Set<String> getSources();
    Map<String,String> getMetaInfo(String key);
    Map<String,String> getMetaInfo();

    void reload();
    boolean isMutable();
}

public interface Configuration extends PropertyMap{

    String getConfigId();

    Boolean getBoolean(String key);
    Boolean getBooleanOrDefault(String key,
                               Boolean defaultValue);
    Byte getByte(String key);
    ...
    <T> T getAdapted(String key, PropertyAdapter<T> adapter);
    <T> T getAdaptedOrDefault(String key,
                    PropertyAdapter<T> adapter, T defaultValue);

    <T> T get(String key, Class<T> type);
    <T> T getOrDefault(String key, Class<T> type, 
                                               T defaultValue);
    Set<String> getAreas();
    Set<String> getTransitiveAreas();
    Set<String> getAreas(Predicate<String> predicate);
    Set<String> getTransitiveAreas(Predicate<String> predicate);
    boolean containsArea(String key);

    Configuration with(ConfigurationAdjuster adjuster);
    <T> T query(ConfigurationQuery<T> query);
}

A configuration instance then can be built using a PropertyMap, e.g.

PropertyMap myPropertyMap = ...;
Configuration config = new BuildableConfiguration
                                       .Builder("myTestConfig")
                   .withUnits(myPropertyMap);

So we can provide partial configurations by just implementing the PropertyMap interface. For convenience an AbstractPropertyMap class can be defined that additionally supports implementing this interface. 

public class MyPropertyMap extends AbstractPropertyMap{
    protected Map<String,String> initContentDelegate(){
      // in reality, provide something useful here...
      return Collections.emptyMap();
   }
}

Using Composites to Build Complex Configurations

Given the simple basic PropertyMap interface we can start thinking on how building more complex configurations by combining existing combinations. Basically the ingredients required are:
  • two (or more) existing configurations
  • a combination algorithm or policy
Now thinking on mathematical sets, we may provide similar functionality when combining configurations:
  • union
  • intersection
  • subtraction
Additionally we have to think ow we should resolve conflicts (different values with the same key), most important policies are:
  • ignore duplicates (keeping the original values from former entries)
  • override existing previous values by later values
  • throw an exception, when conflicting entries are encountered
This can be modeled by a corresponding policy enum:

public enum AggregationPolicy{
    IGNORE,
    OVERRIDE,
    EXCEPTION
}

Finally we can provide a factory class that provides as 
  • commonly used property maps by reading from resolvable paths, using common configuration formats, e.g. .property-files (the resolution capabilities hereby can be extended by implementing and registering a corresponding SPI)
  • most commonly used compositions of partial configurations (maps)
This can be modeled with a simple singleton as follows:

public final class PropertyMaps{

    private PropertyMaps(){ }

    // factory methods
    public static PropertyMap fromArgs(
              Map<String,String> metaInfo, String... args);
    public static PropertyMap fromPaths(
              Map<String,String> metaInfo, String... paths);
    public static PropertyMap from(
              Map<String,String> metaInfo, 
              Map<String,String> map);
    public static PropertyMap fromArgs(String... args);
    public static PropertyMap fromPaths(String... paths);
    public static PropertyMap from(Map<String,String> map);
    public static PropertyMap fromEnvironmentProperties();
    public static PropertyMap fromSystemProperties();

    // combinations
    public static PropertyMap unionSet(
              PropertyMap... propertyMaps);
    public static PropertyMap unionSet(
              AggregationPolicy policy,
              PropertyMap... propertyMaps);
    public static PropertyMap intersectedSet(
              PropertyMap... propertyMaps);
    public static PropertyMap subtractedSet(
              PropertyMap target, PropertyMap... subtrahendSets);
    public static PropertyMap filterSets(
              Predicate<String> filter, PropertyMap propertyMap);
}

With the given mechanism we are able to define complex configurations, realizing some complex override and configuration rules quite easily:

String[] cliArgs = ...;
Map<String,String> defaultMap = ...;

Configuration config = new BuildableConfiguration.Builder(
                            "myTestConfig").withUnits(
       PropertyMaps.from(defaultMap),
       PropertyMaps.fromPaths("classpath:test.properties"),
       PropertyMaps.fromPaths("classpath:cfg/test.xml"),
       PropertyMaps.fromSystemProperties(),
       PropertyMaps.fromPaths(
                  "url:http://1.2.3.4/remoteCfg.xml"),
       PropertyMaps.fromArgs(cliArgs),
      )
      .build();

Basically the above creates a full fledged Configuration instance that:
  • is built from properties contained in the given default map.
  • that may be overridden by entries in test.properties, read from the classpath
  • that may be overridden by entries in cfg/test.xml, using the JDKs xml property format (also read from the classpath)
  • that may be overridden by entries from the resource loaded from http://1.2.3.4/remoteCfg.xml
  • that may be overridden by entries  from the CLI arguments
Of course, this example uses always the same keys for all different partial configuration sources, which might not be a realistic setup. But adding a mapping of provided keys to some other keys is basically a trivial task.

Summary

Summarizing separating configuration into a simple basic interface (PropertyMap) and a more complex extended variant (Configuration), allows us to easily build composite configurations by combining more simpler partial property maps. Most commonly configuration locations, formats and combination strategies can also provided easily by according factory classes. Also in most cases, implementing the more simpler PropertyMapinterface should completely sufficient.
Putting all this to reality, we have defined a quite powerful mechanism, that allows us to implement also complex use cases with only a few abstractions.