PropKeyConst: Property Key Constants Utility

By Mitch Stuart
Copyright © 2005 FullSpan Software  -  Usage subject to license
Software Version: 1.0  -  Document Version: $Revision: 1.3 $, $Date: 2005/04/10 06:04:51 $

Introduction

PropKeyConst is a utility that provides compile-time checking of the property keys used in your Java program. It also provides other property key related features: enhanced development productivity, automated documentation of property keys, and comment preservation during code-based editing of property files.

Java property files are commonly used for messages and other text strings. In this document, we will first briefly discuss the motivation for using property files, and then describe how PropKeyConst can make using them safer and more convenient.

Getting PropKeyConst

Property File Background

Property files are often used to hold text messages. As an example, let's say that we have a Java program that needs to check a password, and if it is incorrect, display the message "Incorrect password". For simplicity, we'll assume that we have a method called displayErr that shows the error message. There are several ways that we could store the error message text.

1. Hardcoded String

The most primitive way is to just use a hardcoded string:
   displayErr("Incorrect password");
The problems with this are:

2. String Constant

The next option is to use a string constant:
   public static final String ERR_INCORRECT_PASSWORD = "Incorrect password";
   . . .
   displayErr(ERR_INCORRECT_PASSWORD);
This is better than a hardcoded string because it is centralized, but it still has the other problems mentioned above.

3. Property File

The next option is to store the messages in a property file. You would have a file, say mymessages.properties, with a line like this:
   err_incorrect_password=Incorrect password
In your code you need to load the properties. There are various ways to do this. One example is:
   ResourceBundle bundle =
     ResourceBundle.getBundle("com/mycompany/myapp/resource/mymessages");
Then when you need the text of the message, you would use code like this:
   displayErr(bundle.getString("err_incorrect_password"));
This solves all of the problems we identified earlier. The messages are centralized and separated from the code, so they can easily be customized and internationalized.

Property Key Mismatch
Using property files is convenient, but there is one major problem with the example above: it uses a hardcoded string as the property key to retrieve the message: bundle.getString("err_incorrect_password"). What if there is a typo in our Java code, or if someone removes or changes the key in the property file but forgets to update the Java file? We will not be able to retrieve the message, so the user will not be properly informed of the error. Furthermore, we do not have any way to detect this "property key mismatch" between property file and Java code condition at compile or build time. It can only be discovered at runtime.

Theoretically, our application test suite should provide 100% code coverage, so we should find this problem during testing. But in reality, very few tests suites provide this level of coverage, particularly for unexpected or environmental conditions (out of disk space, network down, etc.). Thus, the problem may never be discovered until it happens in production. Furthermore, the type of error that is not easily simulated during testing is exactly the type of error where you want to give the best error reporting to the user.

Notice that the property key mismatch cannot be detected by simply introducing string constants. Compare this code:

   String msg = bundle.getString("err_incorrect_password");
to this:
   public static final String PROPKEY_ERR_INCORRECT_PASSWORD = "err_incorrect_password";
   . . .
   String msg = bundle.getString(PROPKEY_ERR_INCORRECT_PASSWORD);
You might prefer the second approach, but it does not solve the property key mismatch vulnerability. There is still no build-time checking that the property key is valid.

Avoiding Property Key Mismatch
PropKeyConst solves the property key mismatch problem by generating a Java class to hold your property keys. By using this Java class, you are guaranteed at compile time that the properties you use in your program actually exist in the property file.

Using PropKeyConst

To use PropKeyConst, you create/edit your property file just as you normally would. Then, at build time, you use the PropKeyConst ConstantGenerator class to parse the property file and generate a Java file with constants for each property in the file. Finally, you write your Java code to use the property keys.

For example, say you have a property file entry like this:

   err_incorrect_password=Incorrect password
PropKeyConst will generate a constant like this:
   public static final String ERR_INCORRECT_PASSWORD = "err_incorrect_password";

Then in your Java code, instead of writing:

   String msg = bundle.getString("err_incorrect_password");
You write:
   String msg = bundle.getString(MyPropKeys.ERR_INCORRECT_PASSWORD);
Because the Java class MyPropKeys is generated at build time from your property file, you are guaranteed that every property key constant within the class refers to an actual property within the property file.

Generating Property Key Constants

The ConstantGenerator class is just a normal Java class that you can run in any way that you wish: in an Ant script; with a shell script or batch file; or inside of an IDE like Eclipse. The important thing is that your build sequence must be:
  1. Create/edit property file.
  2. Run ConstantGenerator on the property file to generate the property key class. This has to be done after the above step, because the properties need to be in the property file before ConstantGenerator can read them.
  3. Compile your Java code that refers to the generated property key constants. This has to be done after the above step, because the constants need to be generated before code that references the constants can be compiled.
Command Line
Here is an example of running PropKeyConst manually. Change to the propkeyconst/propkeyexample directory and run the following command (this should be typed on one line, but is shown on multiple lines here for clarity):
   java -jar lib/propkeyconst.jar
      src/jgenerated/com/fullspan/propkeyexample/simple
      com.fullspan.propkeyexample.simple
      SimpleExamplePropkeys
      src/java/com/fullspan/propkeyexample/simple/resource/SimpleExample.properties
This example shows the following:

Multiple Files
After the four required parameters to ConstantGenerator, you can also specify additional file names if you have more than one property file for which you want to generate constants. All of the property files specified in a single invocation of ContantGenerator will go into the same generated Java class. If you have multiple property files and you want separate constant key classes, simply run ConstantGenerator multiple times, specifying different input file name(s) and output class names each time.

Ant
Now let's look at an example of running ConstantGenerator using Ant. This example is taken from the propkeyexample/build.xml file included with the PropKeyConst distribution:
   <target name="generatePropKeys" depends="init" unless="upToDate.SimpleExamplePropkeys">

   <java jar="${dir.lib}/propkeyconst.jar"
      fork="true"
      failonerror="true">

      <arg value="${dir.src}/jgenerated/com/fullspan/propkeyexample/simple" />
      <arg value="com.fullspan.propkeyexample.simple" />
      <arg value="SimpleExamplePropkeys" />
      <arg value="${dir.src}/java/com/fullspan/propkeyexample/simple/resource/SimpleExample.properties" />

   </java>

</target>
This is identical to the Command Line example shown earlier, except that it uses the Ant sytax for invocation and parameters.
Eclipse
You can generate property keys within Eclipse by using Eclipse's Ant integration to simply invoke the above Ant task. You can manually open your build.xml file in Eclipse's Ant View, and tell Ant to execute the task. However, in most cases you will want to automate the generation of the property keys whenever you edit the properties file. A method for doing this is described in detail in the following section.

Up To Date Checking

Making the generated constants ready for use by your code requires two steps:

Ant


The up-to-date check for the second step is automatically handled by Ant: if the .java file has changed, the .class file will automatically be recompiled. For the first step, we have to help Ant decide if the .java file needs to be regenerated from the .properties file.

Notice that the generatePropKeys target in the above example has the following condition: unless="upToDate.SimpleExamplePropkeys".

This unless condition tells Ant that it can skip the generatePropKeys step if the property keys are already up to date. The upToDate.SimpleExamplePropkeys property is set earlier in the build.xml file:

   <uptodate property="upToDate.SimpleExamplePropkeys"
      srcfile="${dir.src}/java/com/fullspan/propkeyexample/simple/resource/SimpleExample.properties"
      targetfile="${dir.src}/jgenerated/com/fullspan/propkeyexample/simple/SimpleExamplePropkeys.java" />
This test simply compares the timestamps of the .properties file and the .java file. If the .properties file has a timestamp less than or equal to that of the .java file, then the .java file is up to date, and the generatePropKeys target does not need to be executed.

When you edit the .properties file (say, adding a new property or editing the text of an existing one), and then run Ant, the uptodate check will fail. The .java file will be regenerated from the revised .properties file, and then the .class file will be compiled from the .java file.

Eclipse


The propkeyexample subdirectory included with the PropKeyConst distribution contains an Eclipse project. This project's build settings have been set up to automate the property key generation. It works as follows: Notice that all you have to do is edit and save the properties file, and the newly-generated key becomes visible in the Eclipse workspace.

You can view the project properties for the propkeyexample project to see how to enable this automation for your own project. The steps are:

  1. Open the Project / Properties dialog.
  2. Select the Builders item.
  3. Add a new builder of type Ant Build.
  4. On the Main tab: Main tab
  5. On the Targets tab: Targets tab
  6. On the Build Options tab: Main tab Main tab
  7. On the Refresh tab: Main tab Main tab

Property Key to Variable Name Mapping

When PropKeyConst generates the constant for each property key, it does the following: Dotted Names and Hierarchy
Looking in the example SimpleExample.properties, you see lines like this (condensed here for brevity):
   max_free_logins=3
   form.login.title=Login
Then if you look in the generated SimpleExamplePropkeys.java, you see code like this (condensed here for brevity):
   public final class SimpleExamplePropkeys
   {
      public static final String MAX_FREE_LOGINS = "max_free_logins";
   
      public static final class FORM
      {
         public static final class LOGIN
         {
            public static final String TITLE = "form.login.title";
         }
      }
   }
You can see that the property key max_free_logins results in the constant SimpleExamplePropkeys.MAX_FREE_LOGINS.

But notice the property key form.login.title. It results in the constant TITLE. This constant is nested within two inner classes, namely FORM and LOGIN. So in your code when you refer to the property key form.login.title, you would use the constant SimpleExamplePropkeys.FORM.LOGIN.TITLE, just as you would expect.

This mapping from dotted property keys to inner classes has several benefits:

Property File Comments

In a standard property file, comments are denoted by lines that begin with one of the symbols: # or ! as their first non-whitespace character. It is common to document the property file to explain the usage of various properties. This works fine when the file is manually edited, but when the file is read in, processed, and written out, there is no simple or standard way to maintain the comments. For example, the method java.util.Properties.load ignores comment lines. Similarly, the method java.util.Properties.store does not provide any way to emit comments into the property file it creates.

PropKeyConst provides the ability to read and write property file comments. This provides several benefits:

Comment Syntax
Comments for PropKeyConst are specified using the same key=value syntax as normal properties in the property file. There are two defined suffixes that can be used on property keys to indicate to PropKeyConst that the entries are comments, as opposed to "normal" properties. The suffixes are:

Example Comments
Looking in the example SimpleExample.properties, you see lines like this (condensed here for brevity):

   @classcomment=Property key constants for the Music Manager application
   
   form@classcomment=Titles, field labels, validation data, etc. for \
   forms and fields
   
   form.login.field.confirm_new_password@comment=For confirming new password \
   when changing existing user account
   form.login.field.confirm_new_password=Confirm New Password
Then if you look in the generated SimpleExamplePropkeys.java, you see code like this (condensed here for brevity):
   /**
    * Property key constants for the Music Manager application
    */
   public final class SimpleExamplePropkeys
   {
      /**
       * Titles, field labels, validation data, etc. for forms and fields
       */
      public static final class FORM
      {
         public static final class LOGIN
         {
            public static final class FIELD
            {
               /**
                * For confirming new password when changing existing user account
                */
               public static final String CONFIRM_NEW_PASSWORD = "form.login.field.confirm_new_password";
            }
         }
      }
   }
Notice that the @comment and @classcomment property values have been converted to JavaDoc comment text.

If you look in the generated JavaDoc for SimpleExamplePropkeys, you can see the resulting JavaDoc output.

Comment Retention Example
The file PropertyFileGeneratorTests.java shows an example of reading in an existing property file and saving it as a new file, with (PropKeyConst-style) comments retained. Notice that the constants are emitted in alphabetical order, ensuring that comments stay next to their property keys, and related properties stay together.

Limitations of PropKeyConst

There are two main limitations with PropKeyConst that I am aware of. First is that each property key must be a valid Java identifier, which is stricter than the normal standards for property keys. This applies to each component within a dotted property key. The reason for this limitation is that PropKeyConst uses the property key as the Java variable name for the associated property key constant. Examples of invalid properties are: because a number cannot be the first character of a Java variable name, and a quotation mark cannot be part of a variable name at all.

The second known limitation is that the comment text for a PropKeyConst-style comment (using the @comment or @classcomment syntax) must be valid to appear within a Java comment. The reason for this limitation is that PropKeyConst directly copies the comment text into a Java comment block in the generated Java file. Here is an example of an invalid comment:

   max_free_logins@comment=Maximum number of free logins */ before registering
because the */ within the comment will terminate the Java comment and cause a syntax error when the generated file is compiled.