HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

XProperties is a simple subclass of java.util.Properties that allows you to treat property values like constants, referring to and embedding them inside other property values. It handles the substitution of the constant value automatically and transparently when you call the getProperty() method.

Using constants in your property files can make them easier to read and maintain. This article describes an extension to the properties facility in Java that greatly enhances its functionality and usefulness. Like many other Java programmers I use the Properties class extensively for managing configuration information. In an application I recently developed, I had several properties (database URLs) in a configuration property file that shared the same value. I wanted a way to specify a constant in a property file rather than repeating the same value over and over.

My first approach was to add some extra logic where I read in the properties. I designated constant values by an arbitrary prefix ($). As I accessed a property value, I checked if it began with the prefix and, if so, performed the substitution with the named constant value.

I soon realized, however, that I could generalize this capability by moving the logic into a subclass of java.util.Properties. I could also greatly increase its usefulness by not limiting it to a simple, single substitution. Rather, I allowed a constant value to be embedded anywhere within the property value, even allowing multiple constants within the same value. I chose to indicate constant names by enclosing them in braces, abandoning the single prefix character. This makes the constant names obvious and unambiguous, as well as easier to parse.

The following properties illustrate the use of a constant within a property value:

SOURCE = SampleApp
ErrorMessage = {SOURCE}: An error has occurred

The SOURCE property defines a constant value that's embedded within the value of the ErrorMessage property. As I mentioned earlier, to indicate that you wish to substitute the value of another property, surround its name with braces. Notice that the SOURCE property is defined just like any other property value. The only thing that makes it a "constant" is that it's referenced by another value. I've used all capital letters for the constant value, as is the convention in Java, but this is by no means a requirement. If the property name within the braces isn't found, then no substitution is performed and the property value is returned without modification.

You can substitute a constant anywhere in the property value, and even have more than one constant within a value, as in the following example:

CONST_1 = shoes and ships
CONST_2 = sealing wax
SomeValue = {CONST_1} and {CONST_2}
In this example, the "SomeValue" property evaluates to "shoes and ships and sealing wax."

Using constants within property values is especially useful when the same value occurs in multiple places, or when you build up a complex value from several reusable parts (e.g., file pathnames or URLs). Using constants also allows you to enumerate and assign descriptive names to potential property values. You may have to switch frequently between values for a property while developing a system, for instance, the URLs for unit test, system test, and production. One approach I've used is to include a property assignment for each possible value, but comment out the ones not being used. For example:

# MyURL = http://localhost:8080/abc
# MyURL = http://testserver.acme.com/abc
MyURL= http://prodserver.acme.com/abc
Without extra comments within the property file, however, it's unclear what each alternative represents. The extra commented lines also clutter the file, making it harder to read. Instead, by defining each alternative as a named constant, you can assign descriptive names to all the values, making the property files easier to understand, and also lessen the chance of making errors when switching between values. For instance, you can define:

UNIT_TEST_URL = http://localhost:8080/abc
SYSTEM_TEST_URL =
http://testserver.acme.com/abc
PRODUCTION_URL=http://prodserver.acme.com/abc
MyURL = {UNIT_TEST_URL}

In the above example, it's self-evident that the "MyURL" property is set to the value appropriate for the unit test environment. This example is included with several others in the sample property file shown in Listing 1. It illustrates some different ways to use constants within property values. Listing 2 contains Java source code to read in and access that property file using the XProperties class. Listing 3 shows the resulting output from running the code in Listing 2.

Note that the syntax for using the XProperties class - creating an XProperties object, loading the properties from a file, and accessing the property values - is exactly the same as for the java.util.Properties class. This demonstrates the power of inheritance. The XProperties class is designed to be a drop-in replacement for java.util.Properties. No changes or additions are made to the public API of the superclass. The XProperties class handles the constant substitution under the covers when you access a property value using the getProperty() method.

Another point to note is that you can nest the constant substitution to an arbitrary level. The XProperties class recursively evaluates each referenced constant. In Listing 1, the value of the property "MyHtmlFile" includes the value of the property "HTML_DIR". This, in turn, references the value of property "BASE_DIR", which then references "ROOT_DRIVE". You can see in Listing 3 that the value of "MyHtmlFile" evaluates to "c:/base/html/MyFile.html", incorporating all the constants used along the way.

Listing 4 contains the source code for the XProperties class. As you can see, it's not very complicated. The XProperties class overrides the getProperty(String) method. Handling the recursive substitution presented a minor challenge. How do you prevent an infinite recursive loop, as in the following example?

A={B}
B={A}

One option is to perform a complicated analysis to detect loops in the substitution tree. I chose a simpler approach. The XProperties class keeps track of the current depth of substitution (starts at zero and increments with each recursive substitution), and only allows up to an arbitrary (but easily changed) maximum depth - currently five. This logic is implemented in the private getProperty(String,int) method. This method performs the parsing and constant substitution, handles recursive invocation, if necessary, and checks the maximum substitution depth.

Like its superclass, the XProperties class includes a constructor that takes an existing properties object. These are default properties that are searched if a property key isn't found in the original property list. One use for this capability is to define a separate property file containing common constants that can then be accessed from multiple property files. All you need to do is load the property file containing the constants into a properties (or XProperties) object and then pass this object as the parameter to the XProperties constructor.

Be aware that the Properties.list(..) methods don't use the getProperty() method, and so don't perform any constant substitution before displaying property values.

Property files, like programs, can benefit from defining and using constants. You can specify a value once, and then reuse the same value for multiple properties, or you can have property values that are composed of several (potentially reusable) parts. These capabilities can be especially useful in configuration property files in which you need to specify complex values, like file pathnames or resource URLs. You can also enumerate and assign descriptive names to potential values when you need to frequently switch between alternative values for a property, for example, switching between test and production database URLs. The implementation of the XProperties class is fairly simple, but it provides a very useful extension to the properties facility in Java.

Author Bio
Chris Mair is a senior consultant and software craftsman at IBM with 12 years' experience in the financial sector. He's a certified Java developer and specializes in object-oriented design and development, frameworks, and J2EE. cmair@u[email protected]

	



Listing 1: Sample Properties

# Sample.properties
#
# These properties illustrate the use of constants
# within property values, accessed using the
# XProperties class.


# Use several levels of recursive substitution to
# define paths for different resource types
ROOT_DRIVE=c:
BASE_DIR={ROOT_DRIVE}/base
HTML_DIR={BASE_DIR}/html
XML_DIR={BASE_DIR}/xml
IMAGE_DIR={BASE_DIR}/image


# Define full pathnames for resource files
MyHtmlFile={HTML_DIR}/MyFile.html
MyXmlFile={XML_DIR}/MyFile.xml
NewImage={IMAGE_DIR}/new.gif
SaveImage={IMAGE_DIR}/save.gif
PrintImage={IMAGE_DIR}/print.gif



# Assemble a property value from several constants
PROTOCOL=https
SERVER=server.acme.com
PORT=80
RESOURCE=index.html
HomePageURL={PROTOCOL}//{SERVER}:{PORT}/{RESOURCE}



# Define a constant for each possible value
UNIT_TEST_URL = http://localhost:8080/abc
SYSTEM_TEST_URL = http://testserver.acme.com/abc
PRODUCTION_URL= http://prodserver.acme.com/abc


# Select from the possible values
MyURL = {UNIT_TEST_URL}



Listing 2: Sample Java Code to Use XProperties

// import java.io.*;
// Create an XProperties and load the property file
Properties prop = new XProperties();
InputStream in = new FileInputStream("Sample.properties");
prop.load(in);


// List the keys for the property values of interest
String[] keys = {
        "MyHtmlFile", "MyXmlFile",
        "NewImage", "SaveImage", "PrintImage",
        "HomePageURL", "MyURL" };


// Iterate through the keys; display key and value
for (int i=0; i < keys.length; i++) {
        String value = prop.getProperty(keys[i]);
        System.out.println(keys[i]+" = "+value);
}


Listing 3: Resulting Output from Code in Listing 2

MyHtmlFile = c:/base/html/MyFile.html
MyXmlFile = c:/base/xml/MyFile.xml
NewImage = c:/base/image/new.gif
SaveImage = c:/base/image/save.gif
PrintImage = c:/base/image/print.gif
HomePageURL = https//server.acme.com:80/index.html
MyURL = http://localhost:8080/abc


Listing 4: Source Code for XProperties Class

import java.util.Properties;
/**
 * A subclass of Properties that allows recursive
 * references for property values. For example,
 *
 * <pre><code>
 * A=12345678
 * B={A}90
 * C={B} plus more
 * </code></pre>
 *
 * will result in <code>getProperty("C")</code>
 * returning the value "1234567890 plus more".
 *
 * @author: Chris Mair
 */
public class XProperties extends Properties {


   // The prefix and suffix for constant names
   // within property values
   private static final String START_CONST = "{";
   private static final String END_CONST = "}";


   // The maximum depth for recursive substitution
   // of constants within property values
   // (e.g., A={B} .. B={C} .. C={D} .. etc.)
   private static final int MAX_SUBST_DEPTH = 5;


/**
 * Creates an empty property list with no default
 * values.
 */
public XProperties() {
   super();
}


/**
 * Creates an empty property list with the
 * specified defaults.
 * @param defaults java.util.Properties
 */
public XProperties(Properties defaults) {
   super(defaults);
}


/**
 * Searches for the property with the specified
 * key in this property list. If the key is not
 * found in this property list, the default
 * property list, and its defaults, recursively,
 * are then checked. The method returns
 * <code>null</code> if the property is not found.
 *
 * @param   key   the property key.
 * @return  the value in this property list with
 *    the specified key value.
*/
public String getProperty(String key) {


   // Return the property value starting at level 0
   return getProperty(key, 0);
}


/**
 * Searches for the property with the specified
 * key in this property list. If the key is not
 * found in this property list, the default
 * property list, and its defaults, recursively,
 * are then checked. The method returns
 * <code>null</code> if the property is not found.
 *
 * <p>The level parameter specifies the current
 * level of recursive constant substitution. If
 * the requested property value includes a
 * constant, its value is substituted in place
 * through a recursive call to this method,
 * incrementing the level. Once level exceeds
 * MAX_SUBST_DEPTH, no further constant
 * substitutions are performed within the
 * current requested value.
 *
 * @param   key   the property key.
 * @param level  the level of recursion so far
 * @return  the value in this property list with
 * the specified key value.
 */
private String getProperty(String key, int level) {


   String value = super.getProperty(key);
   if (value != null) {


      // Get the index of the first constant, if any
      int beginIndex = 0;
      int startName = value.indexOf(START_CONST, beginIndex);


      while (startName != -1) {
         if (level+1 > MAX_SUBST_DEPTH) {
            // Exceeded MAX_SUBST_DEPTH
            // Return the value as is
            return value;
         }


         int endName = value.indexOf(END_CONST, startName);
         if (endName == -1) {
            // Terminating symbol not found
            // Return the value as is
            return value;
         }


         String constName = value.substring(startName+1, endName);
         String constValue = getProperty(constName, level+1);


         if (constValue == null) {
            // Property name not found
            // Return the value as is
            return value;
         }


         // Insert the constant value into the
         // original property value
         String newValue = (startName>0)
            ? value.substring(0, startName) : "";
         newValue += constValue;


         // Start checking for constants at this index
         beginIndex = newValue.length();


         // Append the remainder of the value
         newValue += value.substring(endName+1);


         value = newValue;


         // Look for the next constant
         startName = value.indexOf(START_CONST, beginIndex);
      }
   }


   // Return the value as is
   return value;
}
}

  
 

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.