Application Configuration, Messages and Exceptions
Introduction
First of all, it is said a picture speaks a thousand words, and an example runs pretty close as well. The following exposition of how we tackled - and hope to tackle - application configuration, messages and exceptions is fairly detailed and involved. The source code for the two subsystems that acted as guinea pigs is available from CVS, as well as examples of configuration files and messages files. A cursory perusal of a working system will pay dividends in understanding. Both Jeff Lusted and Phil Nicolson are available for help, and also of course for constructive criticism.
AstroGrid Iteration 2 saw the genesis of the datacenter and the jes (Job Entry System) work packages, both having a public interface as SOAP services. The packages represent reasonably substantial pieces of work, requiring a great many configuration details for each, and the potential for a large amount of feedback in the way of application messages. The recent (July 2003) refactoring exercise has seen the number of classes total beyond 100 for these subsystems. Early on it seemed imperative that we dealt with configuration details, application messages and exceptions in a systematic and professional way. Quite soon into the development it was also obvious that the services were themselves duplicating aspects that could usefully be held in common. The refactoring exercise has seen some of these aspects being extracted (and hopefully improved) so that they can be reused across the AstroGrid development. The following gives some outline of these developments.
AstroGrid Common Classes
There are now four common Java classes within AstroGrid that deal with configuration, application messages and exceptions. At least one of these must be subclassed to be useful. The classes should be accompanied by a configuration file (xml based), an application messages file (this may be replicated into various languages) and any number of template files (to be explained later). The common classes are:
org.astrogrid.Configurator (abstract)
org.astrogrid.TemplateManager
org.astrogrid.AstroGridException
org.astrogrid.i18n.AstroGridMessage
The supporting files should be appropriately named. I suggest something like
ASTROGRID_subsystemname_config.xml
ASTROGRID_subsystemname_messages.properties
SomeNameRequest.xmltemplate
Configuration.
The main driver is the configuration file. This is a simple xml-based configuration file that provides key-value pairs grouped together in categories. The configuration file is used by the AstroGrid Configurator class to load configuration properties and to provide automated access to a range of application messages. Some of the properties also entail a level of indirection, leading to the loading of further files; at present this is limited to so-called templates, which will be covered later.
The following is an extract for the configuration file for JES:
<?xml version="1.0" ?>
<!-- Each JES is configured by setting properties within this file -->
<properties>
<!-- GENERAL CATEGORY ===================================================== -->
<!-- this is a mandatory category -->
<category name="GENERAL">
<property name="VERSION" value="1.2" />
</category>
<!-- MESSAGES CATEGORY ===========================================================
Following are the base name, language code and country code for
the properties file that contains all messages for a JES
Example.
Given these values:
INSTALLATION.BASENAME=ASTROGRID_jesmessages
INSTALLATION.LANGUAGECODE=en
INSTALLATION.COUNTRYCODE=US
suiteable file names would be:
ASTROGRID_jesmessages.properties // installation default messages
ASTROGRID_jesmessages_en.properties // English language default messages
ASTROGRID_jesmessages_en_US.properties // US specific English language messages
-->
<!-- this is a mandatory category -->
<category name="MESSAGES">
<property name="INSTALLATION.BASENAME" value="ASTROGRID_jesmessages" />
<property name="INSTALLATION.LANGUAGECODE" value="" />
<property name="INSTALLATION.COUNTRYCODE" value="" />
<property name="INSTALLATION.VARIANTCODE" value="" />
</category>
<!-- SCHEDULER CATEGORY =============================================================== -->
<category name="SCHEDULER">
<property name="PARSER.VALIDATION" value="false" />
<!-- Location of our JobScheduler -->
<property name="URL" value="http://hydra.star.le.ac.uk:8080/axis/services/Scheduler" />
<!-- Schedule Job Request Template -->
<property name="TEMPLATE.SCHEDULE_JOB_REQ" value="JobSchedulerRequest.xmltemplate" />
</category>
</properties>
The extract shows three categories, two of which (GENERAL and MESSAGES) are mandatory. The category name and property name are required to retrieve values from the configuration. In the above, the GENERAL category is required for identification purposes, the MESSAGES category identifies an application messages file. The SCHEDULER category groups values required to use the JES JobScheduler service. A good example of extensive use of a configuration to drive dynamic loading of classes as well as database specification details (JNDI, datasources, table names) is contained in the config file for the datacenter (
**attachment here**).
The Configurator class achieves a number of things for you. It is a single-instance class that will load and cache any property values. It wraps another product (jConfig) to achieve this; jConfig in its raw state was not sufficient for our needs. As stated before, there is a certain level of indirection within the config file. The Configurator will:
- (1) Trigger the loading and caching of the application messages file stated in the MESSAGES category, and
- (2) Independently load (and cache) any file whose property name begins "TEMPLATE." and pass the whole file contents back as the property value. This can be particularly useful for xml templates (e.g. for SOAP requests or responses), or in fact for any value large enough to clutter the config file. The TemplateManager deals with this aspect, but is invoked itself by the Configurator, so is completely hidden from view; you need know no more about it.
The Configurator picks up all files via the classpath, so uniqueness of name is important.
To be useful the Configurator must be subclassed. Each subclass enables another configuration file to be automatically loaded and cached, so there is technically no limit to the number of configurations you have access to. However, the recommendation, at least for server-side developments, is that you design one configuration per subsystem (e.g. the datacenter has one config file, JES has another, and so on). The term "subsystem" can be somewhat controversial, but it can also be thought of as a workpackage or application or module. For common tools development, care needs to be taken here. It may be that another mechanism is required, but I would prefer to let this evolve from what we have working at present. It is always difficult to predict.
There is a pattern or protocol to be followed in subclassing Configurator. This is illustrated in the following extract from the DTC class, which is the route into utilizing configuration used by a datacenter. You can choose to name your class what you will, but I suggest you stick to the three letter subsystem acronym. This is hardly meaningful, but it saves a whole lot of typing when retrieving config values, as you will see. In a complex subsystem, you may end up typing this thousands of times.
The only real necessity in producing a subclass is to support the illustrated methods. However, each subsystem needs to provide a three-letter acronym (the reason will follow shortly) and the name of a configuration file (for obvious reasons). The examples we produced for JES and the datacenter also stored all the definitions required to access config values. In the extract below only one of these is provided (for the MONITOR category), but I would expect to see upwards of a hundred of these or more for a complex subsystem. This is, and should remain, a strict protocol. It moves us away from defining these as string constants in open code, which is - apart from being rotten style - very unprofessional.
public class DTC extends org.astrogrid.Configurator {
public static final String
/** Three letter acronym for this subsystem within the overall AstroGrid system...
* "DTC" stands for DaTaCenter */
SUBSYSTEM_ACRONYM = "DTC" ;
public static final String
/** Configuration file for this component. */
CONFIG_FILENAME = "ASTROGRID_datacenterconfig.xml" ;
public static final String
/** Monitor category within the component's configuration */
MONITOR_CATEGORY = "MONITOR" ,
/** Key within component's configuration signifying the xml request template for
* input to the JobMonitor */
MONITOR_REQUEST_TEMPLATE = "TEMPLATE.REQUEST" ;
private static DTC
singletonDTC = new DTC() ;
private DTC(){
super() ;
}
public static DTC getInstance() {
return singletonDTC ;
}
public static String getProperty( String key, String category ) {
return Configurator.getProperty( SUBSYSTEM_ACRONYM, key, category ) ;
}
protected String getConfigFileName() { return CONFIG_FILENAME ; }
protected String getSubsystemAcronym() { return SUBSYSTEM_ACRONYM ; }
} // end of class DTC
Here is a snippet of code showing how the above might be used.
String
xmlTemplate = DTC.getProperty( DTC.MONITOR_CATEGORY, DTC.MONITOR_REQUEST_TEMPLATE ) ;
Application Messages.
The AstroGridMessage class represents a message in a way amenable to internationalization. It utilizes the technique of the java.text.MessageFormat class to merge inserts into a message template. The message templates are held in properties file(s) which can be produced on a per language basis or a per country basis or even a variant within country. Each properties file is loaded as a resource bundle.
We have adopted a two-fold philosophy on messages:
- (1) It is assumed an installation has a default language. Such might be used for displaying logging messages to system administrators. This should be loaded at static initialization time by a major AstroGrid component (Registry, DatasetAgent of the datacenter, JobController in JES). If messages are required on this basis, use the toString() method when printing or displaying. For the first releases of AstroGrid, this method alone should suffice.
- (2) However, astronomers/customers are likely to be from many countries. If messages are required on such a basis, then either
- (a) pass the appropriate language resource bundle to the toString(ResourceBundle) method (you yourself are responsible for loading the correct ResourceBundle) or
- (b) pass the appropriate locale to the toString(Locale) method; in this case the appropriate messages file (differentiated by locale) in line with the messages file declaration within the configuration file is loaded for you.
Message Key and Message Contents.
Following the above, all application messages are held as key/value pairs within standard Java property files. When issuing a message, the AstroGridMessage class will concatinate key and value pair, so that the key appears as first thing in the final message. We have chosen a specific format for key and content that has proven to be a major aid during the iteration 2 development of the JES and datacenter subsystems.
Format of the message key:
AGDTCEnnnnn where
- AG stands for AstroGrid
- DTC stands for DaTaCenter. Your own subsystem acronym would go here.
- E/W/I/Z where E = error, W = warning, I = information and Z = error messages with text hard coded in program. Z-style messages are basically reserved for those situations where it proves impossible to load the appropriate messages' file (and also by implication the configuration file).
- nnnnn is the unique message number (in effect this can be of any length, but see below)
Format of the message body:
class-name: text-of-message
The following is an example of a message entry showing a key/value pair in the messages' property file for the datacenter. For inserts into messages (e.g. {0}), please see the Java documentation regarding the java.text.MessageFormat class. Insert {0} should always be reserved for the class name of the message originator.
AGDTCE00060=:{0}: Could not create datasource for datasource name: {1}, for catalog: {2}
Note. Messages in the range 00001-99999 are reserved to development by org.astrogrid. User-written routines or modules that generate their own messages must follow additional constraints:
- (1) Use only message numbers in the range 100000 or above.
- (2) Attach a unique acronym to the message key that will help distinguish the message from other potential user-generated messages ON A GLOBAL BASIS!
- (3) Ensure user messages are placed at the end of the file.
- (4) Please clearly document the message, or make sure its intent is obvious from the message content.
An example of what a user-defined message might look like:
AGDTCE100023@leicester:{0}: Sort step failure due to insufficient memory. Contact your system administrator. Recommendations: increase memory or make available additional scratch disk space.
AstroGrid Exceptions.
AstroGridExceptions and its derivatives are within the standard approach to exceptions within Java, except (if you pardon the pun) for the ability also to store an AstroGridMessage. The hierarchy for exceptions within the datacenter and JES subsystems shows...
java.lang.Exception
---org.astrogrid.AstroGridException
------org.astrogrid.datacenter.DatacenterException
---------org.astrogrid.datacenter.datasetagent.DatasetAgentException
---------org.astrogrid.datacenter.myspace.AllocationException
---------org.astrogrid.datacenter.votable.VOTableException
---------org.astrogrid.datacenter.query.QueryException
------org.astrogrid.datacenter.delegate.DatacenterDelegateException
------org.astrogrid.jes.JesException
---------org.astrogrid.jes.jobcontroller.JobControllerException
---------org.astrogrid.jes.jobmonitor.JobMonitorException
---------org.astrogrid.jes.jobscheduler.JobSchedulerException
---------org.astrogrid.jes.job.DuplicateFoundException
---------org.astrogrid.jes.job.NotFoundException
------org.astrogrid.jes.delegate.JesDelegateException
I suggest following a similar path wherever possible in other AstroGrid subsystems. Wherever possible, give reasonable feedback either in the form of an AstroGridMessage, or if in an area where AstroGridMessage is felt to be problematic, in the form of an AstroGridException, or a suitable derivative. We should not be shy of developing sofware that gives adequate, and hopefully professional feedback of error, warning or information situations.
Initialization.
You must define a configuration file and one or more message files. You must at least subclass Configurator to make easy use of the system, including automatic initialization. You can look on the latter in two ways:
- (1) Either you rely upon it implicitly and the application may fall over (hopefully tastefully) when something regarding initialization goes wrong. This approach relies very much upon you as a developer handling the situation.
- (2) Or - you invoke a helper routine which will test out that initialization has taken place. The Configurator has such a helper method, that will throw an AstroGridException if there is something basically wrong. We suggest this alternative, which is clearly illustrated in the mainline services of the JES and datacenter subsystems.
Templates.
There are a set of template classes within CVS which are skeletal examples of SOAP services and AstroGrid subsystems. They were meant to be copied and can save you work.
Common.
The AstroGrid common classes are held in the "common" package within CVS.
--
JeffLusted
--
PhilNicolson - 04 Aug 2003