Gene Callahan

Subscribe to Gene Callahan: eMailAlertsEmail Alerts
Get Gene Callahan: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Related Topics: Java Developer Magazine

Java Developer : Article

PropArgs - Every Programmer's Dream

PropArgs - Every Programmer's Dream

A common set of programming problems drove us to develop a Java class we call PropArgs. Consider the following questions a programmer may want answered about a program: Which RDBMS instance should data come from? Does this particular user have any personal preferences I should be setting? Should debugging code be executed during a particular run of a program? Are there different execution paths based on the current operating system? Should the programmer be operating in batch or interactive mode? What directory should disk output be written to?

The common factor in these questions is that they deal with the state of the program when it starts up. Answering the questions involves several problems that usually trouble programmers: saving and then restoring application state and user preferences; the normally painful process of command-line handling (Java doesn't have a standard class to do this); getting access to system properties, application properties and command-line arguments in a uniform manner; setting internal state of objects externally at runtime, that is, without recompilation; and accessing properties as various types (String, int, boolean, Vector, etc.).

Besides the benefits a reusable class for handling these issues brings to an individual program, our system staff saw a benefit in being able to centrally locate and manage companywide properties such as the network location of common servers, and the differences between our production and our test environments. In addition, we wanted to handle common application setup tasks such as parsing special command-line arguments (-?, -debug, -dump, etc.) and providing a standard formatted usage line for every program. By creating a class to handle all these problems, programmers can focus on solving the application problems at hand rather than the same old application start-up issues.

Solution: PropArgs Class
To solve these problems we developed a class called PropArgs (the name is short for "properties and arguments"). A property is a key = value pair of strings. String here means the Java String class at runtime and a simple text string of characters when stored on disk. In our implementation the characters in the key must be alphanumeric characters or periods. The value field may contain alphanumeric characters, spaces or punctuation. At runtime a property value may be looked up and its value converted into some other type. The conversion is done in the PropArgs class based on which of a set of get() calls the application makes. For example, the property size = 10 can have its value retrieved at runtime as a String, an int or a double, depending on whether the application calls get("size"), getInt("size") or getDouble("size"). Thus the application is shielded from having to know how the properties are stored inside PropArgs.

A group of properties is referred to as a property set. A set is named by a quintuple. Commas separate the elements of the quintuple. An element may be undefined, in which case a tilde is used as a placeholder. Each part of the quintuple has a domain. In order, the domains are Environment, Host, User, Application and Instance. ("Instance" allows multiple property sets per application for each user.) An example property set is test,~,~,OptionViewer. This designates properties for the test environment, on any host, for any user, for the OptionViewer application. Note that we don't require the Instance to be mentioned if it's undefined, as it is last and its absence doesn't present any difficulties in parsing the quintuple. Let's look at another example: prod,spica,rob,OptionViewer,1. This set is for the prod environment, on host spica, for user rob, for instance 1 of application OptionViewer.

Property sets are loaded at runtime in a particular order. This allows some properties to override others. As we load sets, starting with the most general and moving to the most specific, programmers are able to inherit common properties, then fine-tune this set for their application for specific users, and even for multiple instances per user, so that users can store multiple configurations. The load order is:

Env, host, ~, ~
Env, ~ , user, ~
Env, ~ , ~ , app,
Env, host, ~ , app,
Env, ~ , user , app,
Env, host, user , app,
Env, ~ ,~ , app,
Instance
Env, host, ~ , app,
Instance
Env, ~ , user, app, Instance
Env, host, user, app, Instance

If a particular set isn't found, it's just skipped, and the next in line is loaded. The special instance of save is used to store properties saved at runtime.

Property overriding is accomplished by simply overwriting older properties with newer values if the keys are the same. For example, if Speed = 19200 is defined in prod,~,~,modem and Speed = 56000 is defined in prod,~,rob,modem, then, if rob is running, the modem application Speed will be 56000, but for all others it will be 19200.

Features
In this section we'll discuss some of the main features of the PropArgs class. It has a rich set of methods for the application programmer.

Command-Line Override
Properties specified on the command line override those loaded from the property database. This is a powerful idea. You can write your classes to have attributes and behaviors controlled from the property database. Nevertheless, runtime behavior of these classes can be modified simply be overriding the property on the command line. No recompilation is needed. We find ourselves putting more and more class configuration into properties for this reason. Also, because all properties are stored in one place, it's easier to administer the company's applications and to control their behavior in a standard manner. An example of command-line overriding: if the property set test,~,~,modem contained the property device = /dev/ser1, to override it at runtime on the command line, you might say:

modem ­device /dev/ser2

That's it! Note that the equal sign is not used on the command line.

Storage
Properties are currently stored in text files, with one property set stored per file. Only one spot in the PropArgs code knows about this location. They could just as easily be stored in a database and retrieved via SQL, or on a remote server and retrieved via a TCP/IP socket. Text files were chosen for ease of implementation and so as not to force all PropArgs clients to connect to a database. Text files also have the benefit of allowing processing by other text-handling tools. (We also provide a GUI for most common property editing tasks ­ the GUI is discussed in greater detail below.)

Handle Different Operating Systems Transparently
There's only one operating system-dependent variable in all our numerous apps and that is in PropArgs. Programmers can specify properties that are different depending on the OS the application is run on. This is done with property keys that begin with an OS name. Ideally you have a key = value pair for each OS you support, for example:

unix.database = /usr/data
win.database = c:\data

At runtime PropArgs determines which OS it's running on and does the following: all key = value pairs that start with the current OS name are kept, but with the OS name stripped off. All key = value pairs that start with other OS names are discarded. What's left is simply:

database = /usr/data

on UNIX , or, on Windows:

database = c:\data

Variable SVARS Substitution
String variable substitution is carried out at PropArgs constructor time. This is a handy way to share values and to save some typing. In the value part of any property you may refer to any previously defined key. Thus:

Foo = bar
Name = $Foo$

will set the value of Name to bar. You can see from this that order of property definition is important. PropArgs maintains the ordering of key = value pairs across saves and reloads.

Static Interface
PropArgs provides a static interface so that classes that aren't directly passed a PropArgs instance at runtime are still able to get access to the application's properties. A static class variable called PropArgs.StaticPropArgs always points to the instance set in the method PropArgs.initStatic(), which is usually called in an application's main(). Any class may then access properties in the following manner:

PropArgs.StaticPropArgs.get("prop_key");

Check() Method
The PropArgs.check(exit) method makes sure that all the properties set in addUsage() calls are present. Currently it checks that a property is present and, optionally, that it has a value. Recall that command-line arguments need not have a value (e.g., -debug). If any arguments are missing or don't have values when they should, a usage line is printed to stderr, and if the exit flag is set, the application exits.

Check() also does a few other helpful things for the programmer. If there is a -debug property present, it turns on the printing of debugging information in our logging class. If there is a -? or -help command-line argument, a usage line is printed. Also, if there is a log.filename property present, it is passed to the logging class.

There is also a method usage() that can be called directly at any time to print the usage line. The addUsage() method is invoked as follows:

Props.addUsage("-file",false,"<filename>");

The second argument denotes whether the property is required. The last argument can be null if the property doesn't have a value but is just a boolean flag.

Put()
The put(String key, String value), together with get(), are the workhorse methods of PropArgs. Put saves properties in an internal hashtable. Some internal debugging and housekeeping activities are done at put() time: saving the source of the property (file, application, internally created) for later debugging, and saving the order of the property (properties must be saved in the correct order). Dollar sign substitution as well as OS stripping is also done during put(), and all include processing.

Get()
The get(String key) is the main method used to retrieve a property's value. The most commonly used version of get returns the value as a String but there are methods to get the value as an int, long, double, boolean, Vector or another PropArgs instance. Most forms of get take a default value, which is returned if the key isn't found. For example,

String boss =
props.get("boss","Rob");

would return "Rob" if no other boss was defined.

Include
PropArgs supports the notion of recursively including other property definitions, much like C or C++. Any key that begins with includeprops or includefile will cause PropArgs to recursively process that property set or file. Since in any single application all keys must be unique, to include more than one file or property set you must append some unique identifiers. Example:

includeprops1 = deve,~,~,ports
includeprops2 = deve,~,~,servernames
includefile1 = /usr/tmp/externalstuff
includefile2 = /usr/tmp/morestuff

Including allows the sharing of properties among many applications. It also makes it easy to change the behavior of multiple applications in one place. For instance, if the name of a common server is changed, you need to edit it only once to update all of your applications.

Save
An important feature of the PropArgs class is the ability of an application to save properties that have been changed at runtime to the properties database. The most common use of the feature is for an application to save user preferences. For example, in some applications the user may be able to set color preferences, window positions, JTable column positions and so on. The user wants to use the same setting every time the application runs. The settings can be saved and then retrieved automatically by PropArgs the next time the program is run.

The saved properties are loaded last (see the load order above), thus overriding any default properties.

GetVector()
A Java Vector can be instantiated from a property by PropArgs if it is of the form:

key = a,b,c,d

Calling:

Vector vec = PropArgs.getVector(key);
will return a Vector containing the elements "a," "b," "c" and "d."

GETPROPARGS()
A new instance of a PropArgs can be created from a key = value property pair if it looks like:

Info = a = b, 1 = 2 , x = z, 3 = 4

Calling:

PropArgs newprops = props.getPropArgs("Info");

will do the job, and then:

newprops.get("a");

will return "b."

Creating a new PropArgs from a single property is a good way to use properties to set and save object states. A class could take a PropArgs instance as a parameter to its constructor and initialize itself from the key = value pairs. You may want to control exactly what properties a class sees, so rather than send it the main application instance of PropArgs, you pass one just for that class. A class can also save its state in a PropArgs instance, which can be turned into a single property in the main application's complete property set. This may seem convoluted, but we use it frequently and it is quite powerful.

PropArgsObjFactory
To save and load more complicated classes to and from properties, we designed a separate class called PropArgsObjFactory. To use this class you simply need to write put() and get() methods to convert your class to and from string form. For example, to store and load the Java Color class as a property, the interface might look like:

Color getColor(String key, PropArgs props)
Void putColor(String key, Color color, PropArgs props)

So a call like:

PropArgsObjFactory.putColor("Button.color", color1, props);

might produce a key = value pair like:

Button.color = 255,100,100

The get method just needs to parse the RGB string and instantiate a Color.

PropArgs GUI
We've written a Java-based GUI to allow developers and system administrators to edit properties. This has made controlling application parameters easy. It's also become an excellent debugging tool. For example, with the GUI, if a user complains of a problem, we can see just what the setup for the application was and possibly fix it directly in the GUI.

Listing 1 is an example of the typical PropArgs setup in main().

Typical application usage might look like Listing 2.

Summary
The PropArgs class has benefited us in several ways:

  • It's sped up application development by making it easy to access all the various properties in the Java runtime environment.
  • It's taken away the drudgery of parsing and checking command-line arguments.
  • It's made it easy to externally control the internal behavior of applications and classes.
  • By centrally locating all the application properties for the entire company, it's easy to make changes that affect all or many applications at once.
If you're interested in using the PropArgs class for your project, the source code is available at www.robdodson.net/java.

.    .    .

Thanks to the other developers who contributed to the design of PropArgs, especially Marlon Guarino.

More Stories By Robert Dodson

Robert Dodson is a software developer who writes options-trading software in Java and C++
for OTA Limited Partnership. Previous projects include weather analysis software, tactical
programs for Navy submarines, and code for electronic shelf labels.

More Stories By Gene Callahan

Gene Callahan, president of St. George Technologies, designs and implements Internet projects. He has written articles for several national and international industry publications.
Rob Dodson is a software developer who writes options-trading software in Java and C++ for OTA Limited Partnership.
Previous projects include weather analysis software, tactical programs for Navy submarines, and code for electronic shelf labels.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.