 |
October 2007 Archives
Loading Properties from XML (revisited)
Posted by felipegaucho on October 29, 2007 at 12:23 PM | Permalink
| Comments (3)
My last
blog about using JAXB instead of Properties for loading configuration of
Java applications was a bit verbose - so, I decided to print a summary
in order to facilitate the comprehension about the original proposal. I
will not expose the discussion again, just present the below comparison
sheet:
| java.util.Properties |
JAXB |
| The configuration files: |
text=word
primitive=2
anobj.name=test
anobj.integer=3 |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myConfig text="word" primitive="2"
>
<my.obj name="test" integer="3"/>
</myConfig>
|
| The code required for loading the
configuration in the memory: |
// Read properties file.
Properties properties = new Properties();
properties.load(new FileInputStream("filename.properties"));
|
// Unmarshal the properties XML file into a Java Object
JAXBContext jc = JAXBContext.newInstance(
AbstractJaxbFootprintStream.FOOTPRINT_CONTEXT, this.getClass()
.getClassLoader());
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setEventHandler(new FootprintConfigValidationHandler());
unmarshaller.setListener(listener);
SchemaFactory sf = SchemaFactory
.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(getClass().getClassLoader().getResource(
"config.xsd"));
unmarshaller.setSchema(schema);
MyConfig properties = (JAXBElement) unmarshaller
.unmarshal(new FileInputStream("filename.xml")).getValue();
|
| Reading the configuration values: |
// A String:
String text = properties.getProperties("text");
// Other primitive values:
int number = Integer.parseInt(properties.getProperties("primitive"));
// An object:
String name = properties.getProperties("anobject.name");
int integer = properties.getProperties("anobject.integer");
Object yourObj = new YourObj(name, integer); |
// A String:
String text = properties.getText();
// Other primitive values:
int number = properties.getPrimitive();
// An object:
Object yourObj = properties.getMyObject(); |
| My arguments for adopting JAXB instead of
java.util.Properties: |
- The red text are hard-codes
occurrences. Even if you create constants, you continue with the
issue to remembers its names, and if you commit a typo or other minor
mistake, nor the IDE neither the compiler will help you to avoid
problems under production.
- Other issue is about creating complex types: you are
responsible for converting the key/values pairs in Java Objects in
case of complex types. You need to check if the attributes required
for creating the object are present and also its types and/or if the
value associated to the key is not null.
- Property files cannot be checked against its syntax or
contents, since it is just a plain text file.
- Complex types should be simulated by a set of key/value
pairs and later converted in a non-bullet-proof code.
- If the application requires different set of properties, it
should rely on several different files. These files are independent
and you must create a custom code to manage it in your application.
|
- No hard-codes, you don't need to check which string map to a
certain object.
- No types conversion by code - the methods you use to
retrieve the values from the Configuration Object are defined with
the correct type.
- There is no risk of deploying typos or minor mistakes since
the compiler and the IDE will notify your mstakes on the fly.
- XML Property files can be verified against its Schema
definition.
- Support for complex types.
- Different set of properties can be modeled in a unique file,
using nested elements. Even if you need to use different XML
properties files, you can import files in a master definition,
creating a formal relationship between them.
|
* The title of this blog is an intentional
reference to this 4
years old article.
Configuration Objects: using JAXB instead of java.util.Properties
Posted by felipegaucho on October 26, 2007 at 03:11 AM | Permalink
| Comments (1)
Configurable features is a common requisite of computer systems,
and the Java API provides the utility class java.util.Properties
to facilitate our lives. It is very simple to use, and it is suitable
for the most part of applications, but it also has some limitations -
more precisely when we consider the configuration data:
- How to check if all key/values are really present in the
properties file?
- How to check if the type of the values are consistent with the
types required by the application?
- How to represent complex types (nested properties)? For
example: I have a property that is a set of properties.
- How to represent values that are not Java primitive types?
Special remind: properties loading using the java API just reads
the key/values pair in the memory , it doesn't check its consistency or
any data structure related to the configuration. And more: unfortunately
the Properties class is locked in the old Properties DTD and it doesn't
support nested structures or complex types. It also provides a XML
validation mechanism, but it is really too simple to be useful in more
complex scenarios - like the one I present below.
A real world example where java.util.Properties
seems not to be enough
My need for a better Properties loading mechanism started when I
coded the Footprint
project - Footprint is a signed PDF generator that requires three
distinct set of Properties:
- The JDBC Driver Properties: required by
java.sql.DriverManager#getConnection
- A mapping between the name of the fields in the PDF template
and the name of the column names in the database.
- The general properties used to configure the system (number of
threads, resources directories, etc.).
My prime code was based on three different files, and I also
thought of creating a unique properties file referring the other ones or
adopting some pre-defined prefixes/sufixes in order to identify the
different set of properties - like jdbc.prop1=value. After
some analysis it became clear my system required a more sophisticated
configuration mechanism than a collection of key/value pairs of strings.
It requires nested structures and better yet if it can have validation
of its configuration data. Natural conclusion: these requirements can be
easily achieved with an XML file. After deciding to use XML, I evaluated
some options, including different XML frameworks and also the usage of
XML with the Java API. I also checked the new features of JAXB 2.0, and
the fact it is already distributed with the JRE 1.6 convinced me that
JAXB is a first class configuration binder. The figure below summarizes
my observations about property files versus XML:
| Option #1: several Properties |
Option #2: XML file converted in Configuration
Object through JAXB |
 |
 |
- Good:
- Plain text file
- Easy visualization of key/values pairs
- Simple code for reading the properties
- Default Java API - required classes are distributed as part
of JRE (since 1.2)
- Bad:
- Several files to manage
- Weak validation of files structure and contents
- Text editors or IDEs don't help to identify problems
- String + primitive types representation only
- Error prone - it is very hard to visualize missing data
- Custom binder - it requires a custom code to convert String
values in Java Objects
- Inclusion of new key/pair on demand - hard to control the
mess in big teams
|
- Good:
- Plain text file
- Only 1 file to manage
- Configuration data defined in a model
- Validation support by default using the XML Schema
- Configuration versioning
- Data types and values range configurable in the model
- Any Java type supported
- Default Java API - required classes are distributed as part
of JRE (since 1.6)
- Full support from IDEs in editing time (highlight,
validation and auto-complete)
- Missing data never happens
- Automatic binding between the configuration values in Java
Objects
- Inclusion of new properties depends on model changing -
more easy to control the mess in big teams - just keep the model
locked ;)
- Bad:
- Difficulty in visualizing the information
- Any new configuration value impacts in a model changing
|
Implemented solution: binding XML properties to Configuration
Object through JAXB 2.0
The full source code I used to implement my JAXB based properties
mechanism is available at the Footprint
SVN repository - including the ANT
task required to create the XML<->Object binder using XJC. I strongly
recommend you to download the footprint source code and try it a bit
before checking my conclusions at the end of this blog. Inspecting the
code you will start seeing how clean is the application regarding to its
configuration properties. I don't have hard-coded String representing
the configuration acronyms, and I don't need to verify if the values in
the properties are consistent with the values required by the code, it
is implicit since we are calling the methods of the Configuration
Object that are homonymous to the properties names. Other comfortable
feature: if I modify the XML schema or try to change the code in a way
both sides become inconsistent, the IDE will notify the problem,
avoiding me to crash my own code - the code will not compile before the
classes and the properties model become consistent to each other, even
under that stressful deadline :)
First step: you first need to create a XML
Schema, and then use an ANT task to generate the XML<->Object
binder classes, like the one below:
<taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask">
<classpath refid="classpath.base" />
</taskdef>
<target name="compileschema">
<echo message="Compiling the Curriculum XML Schema..." />
<mkdir dir="${jaxb.classes.dir}" />
<xjc schema="${schema}" extension="true"
classpath="${build.dir.classes}" target="${generated.dir}"
package="${jaxb.bindingclasses.package}">
<produces dir="${jaxb.classes.dir}" includes="**/*.java" />
</xjc>
<javac srcdir="${generated.dir}" destdir="${generated.dir}"
debug="off" source="1.5" />
<jar destfile="lib/footprint-config.jar" basedir="${generated.dir}"
excludes="**/*.java" />
<delete dir="${generated.dir}" />
</target>
Before discussing the pros and cons of the JAXB adoption, let me
show some code fragments. What we need to do is to load and to use the
configuration properties, what is quite easy with the newest version of
JAXB. The validation of the XML against its schema is automatic, but I
can also implement a finer verification of the properties data using the
Event Callbacks mechanism provided by JAXB.
- A code for reading the XML properties file:
@SuppressWarnings("unchecked")
public JAXBElement read(InputStreamReader inputStream)
throws Exception {
JAXBContext jc = JAXBContext.newInstance(
AbstractJaxbFootprintStream.FOOTPRINT_CONTEXT, this.getClass()
.getClassLoader());
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setEventHandler(new FootprintConfigValidationHandler());
unmarshaller.setListener(listener);
SchemaFactory sf = SchemaFactory
.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(getClass().getClassLoader().getResource(
AbstractJaxbFootprintStream.SCHEMA_FILE));
unmarshaller.setSchema(schema);
JAXBElement footprintProperties = (JAXBElement) unmarshaller
.unmarshal(inputStream);
return footprintProperties;
// That's it! From this point I have a Java Object I use to retrieve the configuration values
}
- After binding the properties to Java Object, I can manipulate
its attributes as any other class:
private void DemoMethod(FootprintProperties properties) {
ConfigEmail email = config.getEmail();
setMsgFrom(email.getMsgFrom()); // GOODBYE hard-coded strings like prop.get("msg.from");
}
- An optional Unmarshal
Event Callbacks, to check what is being read. Observe in the code below
that I can inspect every element of my configuration, which means I can
stop the application loading if I detect something inconsistent. I am
using this listener also to log what is being loaded. And since I have
a model, I know what any fragment of my configuration file is
about and I can do extra verification on the config values. Eventually
I can also modify the created Object based on custom requirements.
@Override
public void afterUnmarshal(Object target, Object parent) {
if (target instanceof ConfigEmail) {
validateEmailData((ConfigEmail) target);
}
...
}
// Observe I don't need to check if the msgTo attribute is present or not
// in my property file, the model guarantee that.
private void validateEmailData(ConfigEmail email) {
String msgFrom = email.getMsgFrom();
Pattern p = Pattern.compile(".+@.+\\.[a-z]+");
Matcher m = p.matcher(msgFrom);
boolean matchFound = m.matches();
if (!matchFound) {
severe(FootprintConfigUnmarshallerListener.I18N_KEY_EMAIL_MALLFORMED,
new String[] { msgFrom });
}
}
Conclusions
The implemented code works fine, and despite I didn't stressed
too much the tests, I have some observations:
- The loading performance is good, since a regular configuration
file is never more than hundreds of values.
- Memory impact: large sets of configuration can cause an
explosion of Objects in the memory, and that is a good thing to keep in
mind: each element of the XML will instantiate a different Object.
- The complexity of the reading/writing algorithm is similar do
the one required by the
java.util.Properties, and after
creating it once you can reuse it later.
- The validation and verification of the configuration data are
sound and robust processes - much more elegant and safe than using
properties files.
- Open the configuration example XML in your preferred IDE, and
you will notice the helpful support available there, including
highlight and auto-complete features.
- Think about the natural match between the XML structure and
the beans editors code out there. It is quite easy to create a GUI that
presents to the user a tree with all configuration elements and its
respective editors. I started a Swing
prototype here, but it is still a pending task. Eventually I will use
the NetBeans platform, that gives me a wonderful beans editor support,
or even try JavaFX to produce the configuration editor GUI.
A polemic topic is about the large amount of Objects loaded in
memory, what is controversial if you consider what happens when you load
values using java.util.Properties. The properties are
loaded as Strings, what in the worst case means one String object to
each value. Even considering Strings are lightweight objects compared to
complex types, all Strings will be used to instantiate an Object at some
part of your code. One can suggest JAXB is much heavier since it loads
all objects at once, while properties being used by demand will rarely
cause all objects being loaded together. I agree in terms, but I think
JAXB provides a better productivity - reducing the chances of mistakes
and facilitating the verification of the configuration and the code.
Imagine a developer using auto-complete for loading the properties,
instead of checking constants somewhere or including weird hard-coded
String all over the code. It is up to you to think about that.
My overall feeling about using JAXB instead of Properties is very
good, I am feeling much more confident to code based on Objects than
based on collection of Strings. At the end, both approaches are quite
similar in terms of logic, but since I am an Object Oriented developer,
I prefer to manipulate objects than lists of values - the old fashion
procedural approach.
|