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.
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>
<? 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:
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=myPropValuepersistence-unit.myapp.provider=\
org.hibernate.ejb.HibernatePersistence
persistence-unit.myapp.jta-data-source=java:/DefaultDS
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.