Java applications are typically deployed in multiple environments and platforms, each requiring some unique configuration. JFig gives developers a simple yet powerful tool to manage their applications’ configuration. It allows them to:

1. Store application configuration in one common repository of XML files

2. Access configuration data using one common, convenient interface

3. Easily define multiple configurations, dynamically modifying those variables that need to change in different situations

4. Eliminate the error prone practice of defining the same configuration variables in multiple locations

5. Ease the management, deployment, and control of configuration files

 

THE PROBLEM

Most projects require a unique configuration for each version of that project. Your project may use a different JNDI address on Production than on Test and a different Logging level on Development. You may have multiple developers connecting to separate database instances, each with their own password. Files may reside in one directory on Windows, another directory on Unix. A customer may need to customize certain parameters in their installation without modifying the base configuration.

Also, many applications span multiple JVMs, yet they still have a need to share a common configuration. A client running in IPlanet needs to communicate with the domain running in Weblogic using a common JNDI address. Changing that address in one place and not the other breaks the application. Add to that the complexity of using different JNDI addresses in Production and Development, and you have a maintenance nightmare.

At the same time, the great majority of configuration parameters are unchanged across multiple environments.

Many projects use one or more property files to define configuration parameters. There are many problems inherent in this approach. If you want to have different values depending on your environment you have several choices, none of them good.

1.      Modify your configuration files every time you deploy in a new environment.

This is error prone and time consuming, especially if you are building often.

2.      Maintain separate copies of configuration files in different locations with the same file name.

This means you can’t have your configuration files in your source control repository since they have the same name. It also means that when you change one, you need to change every version of that file in every location. That is a more difficult task since the files may be in different locations and are not tracked by version control.

3.      Maintain separate copies but with different names.

You’ve solved the version control problem, but you still need to change every version when you change one.

4.      Some people use Ant to help manage configurations. While Ant has some of the features of JFig (property and variable substitution) Ant lacks the hierarchical capabilities of JFig. In Ant properties are immutable and one can not add one Ant XML file on top of another. In fact, most people end up driving Ant with property files, which brings us back to the original problem.

When clients install applications remotely, the client is often required to make modifications to the configuration file. Every time they get a new version of the application, they need to refit their local updates. This too is error prone and time consuming.

Often, configuration parameters are stored in property files. But property files are java specific. If the same configuration parameters are needed in a non-java environment, or even scripts used to control a java environment, even more configuration files may be necessary.

Another major problem occurs when the same value is defined in multiple property or configuration files. When it comes time to change the JNDI address, it may be necessary to change it in several places or the client stops talking to the business domain. One of the reasons for this is property files and other configuration implementations don’t lend themselves to reuse.

 

THE SOLUTION

What is needed is a hierarchical approach to configuration files. The ninety percent of configuration values that do not change can be maintained in a base file. The other ten percent (or less) may be maintained in their own distinct configuration file. At run time, the files are layered on top of each other to provide a flexible, manageable configuration. For example, in a development environment myhost.config.xml combines with dev.config.xml and base.config.xml to form my unique configuration.

Each configuration file may then be maintained in version control as they have unique names. Only the base files need to be modified when base values change, and it is easy to see the difference between versions. Another major benefit is that changes to the base configuration file will be exhaustively tested before deployment.

Often part of a value may change, but not the entire value. Many values may follow this pattern. For instance, in application X, development files are written to …/dev/files. Support emails go to devsupport@xcompany.com, with a subject line of “dev error”.  In production, files are written to …/prod/files. Support emails go to prodsupport@xcompany.com, with a subject line of “prod error in application X.” The only thing that has really changed is the words “dev” and “prod” There is really only one change here, not three. The use of substitution variables enables us to accomplish this with only one entry.

In the case of applications installed by clients, if the client were to make their modifications to a client configuration that inherited from the base configuration, it would eliminate the need to refit the client modifications when the product is upgraded. It would also be easier to see exactly what changes the client made, making it easier to debug configuration problems.

JFig provides many of these solutions. It allows for a combination of a hierarchy of configuration files, substitution variables and property variables. Methods are provided to get values stored in a configuration dictionary with a variety of types (String, array, integer, float, boolean, etc) and default values.

After some initial setup, the end result will be a cleaner, more easily maintainable application.

 

 

USAGE

The class JFig implements the public interface for the configuration object. To get a singleton instance of this object, use JFig.getInstance(). To get values from the config object, use JFig.getInstance().getValue(sectionName, key) Or JFig. getInstance().getValue(sectionName, key, defaultValue) If you use a default value and the section/key is not found, the default value will return. If you don’t use a default value and the section/key is not found, ConfigException is thrown.

To set JFig values, use JFig.setConfigurationValue(sectionName, key, newValue);

For the most part, however, config values are set during the initial parsing of one or more ini files.

To show a complete listing of JFig values, use JFig.getInstance().printConfigurationDictionary()

 

SETTING UP THE CONFIGURATION FILE(S):

There are two formats for specifying configurations. One is XML based, the other is standard INI file format.

 

XML Format

<configuration>

<include name=”base.config.xml”/>

      <section name=”locos”>

<entry key=”instance” value=”development” />

      </section>

<section name=”Paths”>

<entry key=”locosHome” value=”d:/[locos]{instance}/project/” />

<entry key=”locosExternal” value=”d:/external/[locos]{instance}/” />

</section>

 

<section name=”attachments”>

<entry key=”attachmentDirectory” value=”[paths]{locosExternal}attachments/”/>

     </section>

</configuration>

INI File Format

Section names are in brackets, followed by key/value pairs.

Following is a sample file:

[project]

instance=development

timeout=5

 

[Paths]

locosHome=d:/project/[locos]{instance}/

locosExternal=d:/[locos]{instance}/

 

[attachments]         

attachmentDirectory=[paths]{locosHome}attachments/

 

 

The first section is very straightforward. A call to

getValue(“project”, “instance”) returns value “development”.

 

 

SUBSTITUTION WITHIN ONE CONFIGURATION FILE

The second value, section Paths, key locosHome, uses the value of  project, instance to build its value. So, locosHome resolves to d:/project/development . The next entry, attachmentDirectory, uses the previous entry to build its value. Thus, attachmentDirectory resolves to d:/project/development/attachment.

This comes in very handy when there are many values that have common relative paths or some other value in common. The root value can be changed once without having to change every value.

 

SUBSTITUTION VARIABLES

As shown in the above sample file you can “build” values using substitution variables.  Substitution variables are in the format: [section]{key}

 

NULL VALUES

You can set a value to null by simply omitting the value=”” attribute. All rules for overriding values with multiple files still apply.

 

SYSTEM PROPERTY VARIABLES

You may also access system property variables using the syntax: value=”$xxx$” . You can use this to dynamically build values such as classpaths or file paths based on the settings of your environment.

The following example shows how to retrieve the user.home environment variable as well as use it to construct another directory name.

<section name=”paths”>

                        <!-- Example using existing system properties -->

<entry key=”homeDir” value=”$user.home$” />

<entry key=”docomentDir” value=”$user.home$/documents” />

</section>

 

String documentDir = JFig.getInstance().getValue(“paths”, “documentDir”);

Returs (for me):

C:\Documents and Settings\conrad4/documents

 

 

 

 

SETTING SYSTEM PROPERTY VARIABLES

If you define a section named, “properties”, those values will be set as property values which will then be available to the rest of your application. In the following example, after JFig is initialized, getProperty(“riley”) will return “beagle”.

<section name=”properties”>

<entry key=”bruce” value=”conrad” />

<entry key=”riley” value=”beagle” />

</section>

This can be especially valuable for setting configurations for other applications out of your control that do not use JFig. For instance, you can have a “wrapper” program that calls JFig to set property variables, then invoke another java class (like Ant or Weblogic) that uses properties inside their application. This enables you to reuse your JFig configurations as a single point of control for other classes within the realm of your application. (See AntFig and MavenFig.)

 

 

Additionally, JFig provides the capability to retrieve the contents of an entire section as a Properties object. You may supply your own Properties object (including the System Properties object) or let JFig create a new one for you. See the java docs for more information.

 

MULTIPLE CONFIGURATION FILES

One of the most useful aspects of JFig is the ability to layer multiple configuration files. This can be very helpful as you can maintain one stable base configuration file and make modifications to other config files. The “include” directive instructs the JFig parser to find the included configuration file. Files are parsed in reverse order. Thus, if a prod config file includes a base config file, the base config is parsed first. The prod config will be parsed next and any duplicate section/key values will overlay those in the base config file.

Note: The ini format has not been upgraded to recognize the include directive.

You can maintain a base configuration file for an application while allowing users to modify a local or site copy. That way, the base configuration file is unchanged and it is easy to see what values are overridden. If you upgrade the base configuration file, local overrides do not need to be re-implemented. Developers could take advantage of this by making their changes to a test configuration file, leaving the base unchanged and making it very obvious as to what is being overridden.

 

COMMENTS AND CONTINUATION LINES

Other syntax rules are “*” for comments and “,” for continuation lines (This pertains to ini file format only).

 

HELPER METHODS

The most used method, getValue, returns its value as a String. getIntegerValue, getFloatValue, getLongValue, and getBooleanValue will return values in the corresponding primitive types.

Additional helper methods are getArrayValue which parses values separated by commas, semicolons, or spaces into a String array.

getValuesStartingWith returns a List of values starting with the specified Key/Value.

See the javadoc for more information.

 

CASE INSENSITIVITY

JFig is case insensitive. While values retain their case, the keys to access them will work regardless of case. So, if, the section is “aSection” and  the key is aKey”, the value may be retrieved using AsEcTiOn, AkEy or any other case pattern. There is a slight performance benefit if you do use the correct case.

 

STARTUP PARAMETERS

Note: See section on overriding JFigLocator to provide your own scheme for locating configuration files.

Two parameters tell the JFig package what configuration file to look for and where to find it. These are:

·        config.filename

o       Specifies the name of the starting configuration file to use.

Default value is <hostname>.config. xml.

·        where hostname is the name of the current machine. Please note that the hostname is converted to lower case. This has no effect on Windows but it could on another OS.

·        config.location

·        config..location=classpath tells JFig to look in the classpath for the configuration file.

 

config..location=file tells JFig to look in the file system for the configuration file.

Startup Examples:

1.      Process JFig file prod.config.xml in the file system.

 

·        java –Dconfig.filename=c:/project/xyz/prod.config.xml –Dconfig.location=file com.domain.StartServer

 

 

 

2.      Process JFig  file prod.config.xml in the classpath. Note that these two are the same as classpath is the defaulf config.location.

 

·        java –Dconfig.filename=prod.config.xml –Dconfig.location=classpath  com.domain.StartServer

·        java –Dconfig.filename=prod.config.xml com.domain.StartServer

 

 

 

3.      Process JFig  file hostname.config.xml in the classpath, assuming we are running on host named prodserver. Note that these three are the same as classpath is the defaulf config.location and prodserver..config.xml is the default config.file on host prodserver

 

 

·        java –Dconfig.filename=prodserver..config.xml –Dconfig.location=classpath com.domain.StartServer

·        java –Dconfig.filename=prodserver.config.xml com.domain.StartServer

·        java com.domain.StartServer

 

 

 

INITIALIZATION

 

JFig can be explicitly or lazily initialized. We at JFig Industries recommend an explicit call to

 

JFig.initialize()

-         or –

              JFig.initialize(JFigLocatorIF)

 

·        See section on overriding JFigLocator to provide your own scheme for locating configuration files.

 

A call to initialize will throw a JFigException if any of the specified configuration files are not found or anything else goes awry.

 

Another alternative is a call to

JFig.getInstance()

-         or –

JFig.getInstance(JFigLocatorIF)

 

If the initialize method has not been invoked, the first time getInstance() is called the JFig object lazily initializes the configuration files and builds the configuration dictionary. Subsequent calls retrieve values from the dictionary. However, JFig.getInstance() does not throw an exception as we didn’t want to force users to catch exceptions on every call to JFig. If there are problems in the startup, an error message is printed and an empty JFigIF object is returned.

 

REPROCESSING and JFIG LISTENERS

To reprocess JFig on a running system, call JFig.getInstance().reprocessConfig().

If you have made changes to your configuration files, these changes will be reflected in the current running configuration. You may want to use a jsp to invoke this call. Examples are available.

Classes implement the JFigListener interface to detect when configuration changes have been made via reprocessConfig(). For example, your code may have Scheduler object that initializes its schedule parameters using JFig. However, if you wish to have the flexibility to change these parameters, you may have your Scheduler implement JFigListener. Then when you change the parameters in your configuration file and run reprocessConfig(), the Scheduler will know to reprocess its schedule parameters.

In another case, you may have a Thread that makes a call to JFig to determine how long to sleep. If it calls once at startup and caches that value, it will need to implement JFigListener to know if the value has changed. If it calls JFig on each iteration, there would be no need to implement JFigListener as it will get the changed value on the subsequent invocation.

 

USING JFIG WITH OTHER APPLICATIONS

 

In a perfect world all applications would use JFig. This would allow us to combine all our configurations into one common repository (but not one file) and eliminate the need to define the same variables over and over. But, as we all know, this is not a perfect world. However, there are ways of using JFig to configure some other applications.

 

Applications like Weblogic allow users to pass properties at startup. A two line java program that calls  JFig to define system properties (see section SETTING SYSTEM PROPERTY VARIABLES), followed by a call to Weblogic eliminates the need to manage Weblogic startup properties in startup scripts, property files or otherwise. It would be practical to leave the rest of the Weblogic configuration that never changes and is used only by Weblogic in the Weblogic configuration files. Those values that are shared and/or benefit from dynamic configuration are best managed by JFig.

 

Maven is another example of an app that would benefit from JFig configuration management. Maven is a great tool and even has some ability to “subclass” configuration files. However, its scheme of various property files all with the same name but in different explicit locations (home directory, maven directory and one or two other places) is not one of its strong points. Plus, it’s just one more place to replicate many of the variables that are defined in other places in your application. The java class, MavenFig, is provide with the JFig distribution. It is simply the two lines mentioned above, a call to JFig followed by a call to Maven.

 

AntFig, also provided in the JFig distribution, is similar to MavenFig and enables you to consolidate your configuration variables under one umbrella. While Ant has some of the features of JFig (property and variable substitution) Ant lacks the hierarchical capabilities of JFig. Ant allows you to include other files but properties are immutable. The first one wins instead of the last.

Log4J fits in very nicely with JFig. It allows property value substitution within its configuration files much the same as JFig. So a call to JFig might set properties to define different log levels for your production, development, or test environments or different directories to store log files. A subsequent call to Log4J configure() methods and you have JFig’ed Log4J.

Applications that don’t provide a properties interface are not as easily configured by JFig. If you are truly looking for centralized configuration nirvana, you can get closer. It may be possible to obtain a handle to their configuration object and make calls to set those properties that are best managed by JFig (those that you want to manage centrally and/or dynamically).

 

Still another alternative is using a call to JFig’s convert(inFile, outFile) method. The call to convert allows you to define a given configuration file in a template using JFig substitution syntax (see sections SUBSTITUTION VARIABLES and PROPERTY VARIABLES). If, for example, you want to use https in production but http in development, you can define a JFig-like variable (ie [struts]{httpProtocol} ) in a struts-config.template. A call to convert(a struts-config.template, a struts-config.xml) prior to struts initialization will achieve the desired results.

(Caveat Emptor: JFig convert is a new feature beginning with version 1.4 and has not been used in any application at this point).

 

PROVIDING YOUR OWN JFigLocator

JFig has it’s own scheme for locating the initial and subsequent config file. If no filename or location is specified, JFig looks for file <hostName>.config.xml in the classpath.

Users can now write their own implementation of JFigLocatorIF to use their own logic for locating config files. They may subclass JFigLocator and selectively override individual methods such as getConfigFileName(),  getDefaultConfigFileName(), and/or getInputStream(). Or they may write their own implementation from scratch.

To use your own JFigLocatorIF, instantiate it and pass it to the initial call to JFig.

JFigLocatorIF locator = new MyJFigLocator();

JFig.getInstance(locator);

 

First call to JFig is:

JFigLocatorIF locator = new LocalJFigLocator();

JFig.getInstance(locator);

-         Passing an instance of your implementation of JFigLocator

·        All subsequent calls use normal:

JFig.getInstance() …

 

 

PERL IMPLEMENTATION

There is a perl implementation of JFig. This allows us to write perl scripts to start, stop, or otherwise control a java environment using many of the same configuration values.

PERFORMANCE

To be written…but it’s fast.

GUIDELINES

To be written

Don’t layer more than 3 or 4 configuration files.

 

 

PREREQUISITES

As of JFig 1.5, we are using the standard JDK classes for Regular Expressions and XML parsing. The only external jar is Log4J.

For JFig versions prior to 1.5, the following jars are necessary. The listed versions work.

Xerces – either xerces-1.2.3.jar or both xercesImpl.jar and xml-apis.jar

Jakarta-regexp – Jakarta-regexp-1.2.jar

Log4J – log4j-1.2.6.jar

 

 

FUTURE ENHANCEMENTS

To be written.

Manage multiple configurations via ConfigFactory