|
Validation Errors
If your application has a user interface, then you'll need to standardize the way validation errors are handled and reported. If you've got a Web interface built with
Struts, you could use the Struts
Validator. If you plan to code your own validation modules, then do it early on and include ready-to-use code for at least these checks:
- Optional/mandatory
- Length of a string
- Numeric (whole and fractions)
- Numeric ranges
- A list of (string) values
- Dates
Bug/Issue Tracking
When you place your application in production, it's, of course, necessary to have a bug registration and tracking system available. However, issues of all sizes will pop up during application development, and it's easy to forget them if you don't have a simple and reliable way of writing them down. Anything is better than nothing, even a Word document can be used. For more serious issue tracking, take look at tools like Bugzilla.
For each issue, enter:
- Issue date
- Name of the person registering the issue
- Priority-level
- Person to whom the issue is assigned/li>
- A short description
- A longer, detailed description
- Issue status (i.e., "OPEN," "WORKING," "CLOSED")
Building a Logging Framework
The well known logging framework,
Log4J, and also
the logging API in JDK 1.4, both demand that you select a logging severity level "debug," "info," "warning," and "error." It's important to consider which logging severity should be used in every situation, but it would be nice if severity levels could be defined in a configuration file, like so many other application parameters. The requirements for such a solution would be:
- The configuration file should be in XML format.
- Every logging message should be identified by a key.
- Every message should contain the severity level and a message text.
- It must be possible to insert actual data values in the message text.
The XML format might look like this:
<?xml version="1.0" encoding="UTF-8"?>
<messages>
<message>
<key>TestINFO</key>
<severity>INFO</severity>
<text>This is a test info message</text>
</message>
<message>
<key>TestWARNING</key>
<severity>WARNING</severity>
<text>This is a test warning message</text>
</message>
<message>
<key>TestERROR</key>
<severity>ERROR</severity>
<text>This is a test error message with data: {0}</text>
</message>
</messages>
The last message shows the syntax for inserting actual data values in the message text. {0}, {1}, and so on are be used to mark insertion points in the text.
A nice API is easy to make, based on such a message file:
logger.log("TestINFO");
logger.log("TestWARNING");
logger.log("TestERROR", "my data");
The first parameter to the log method is the key from the message file. If the message needs data inserted, then these data values are specified as extra parameters.
How can you buils such a logging API? Well, you can use the Log4J package, the JDK 1.4 logging API, or you can even build on top of another layer like the Jakarta Commons Logging package. To learn how to make a real implementation, take a look at the following example using the JDK 1.4 API.
The class, called MyLogger, which offers the log-methods will have these responsibilities:
- To read and store data from the XML message file.
- To fetch the proper message when a log method is called.
- To handle substitution of data in the message texts.
Many utilities are available to read and parse the XML file. This example
uses the Commons Digester package. This is a good choice for this example because the Commons Digester makes it easy to build an object structure that suits this API's needs. To learn more about using Digester, take a look at the article "Digesting
XML Documents."
The XML file should only be read once, and it's convenient to store the messages in a HashMap, with the message-key as the key to the HashMap. So, the first time the MyLogger class is loaded, it will read the XML file and create the HashMap:
. . .
private MyLogger() {}
// A JDK logger for internal use
private static final Logger basicLogger = Logger.getLogger(MyLogger.class.getName());
// The message file
private static File input = new File("logMessages.xml");
// All log messages
private static Messages messages = getMessages();
// The "real" logger
private Logger logger;
private static Messages getMessages() {
Digester digester = new Digester();
// Setup Digester
digester.addObjectCreate("messages", "dk.hansen.Messages");
digester.addObjectCreate("messages/message", "dk.hansen.Message");
digester.addBeanPropertySetter("messages/message/key");
digester.addBeanPropertySetter("messages/message/severity");
digester.addBeanPropertySetter("messages/message/text");
digester.addSetNext("messages/message", "addMessage");
// Parse the XML message file
Messages messages = null;
try {
messages = (Messages) digester.parse(input);
} catch (IOException e) {
basicLogger.severe("The log messages file could not be read. Exception was: " + e);
System.exit(1);
} catch (SAXException e) {
basicLogger.severe("Failed reading the log messages file. Exception was: " + e);
System.exit(1);
}
return messages;
}
. . .
The complete class and all helper classes are available for download in the resources section at the end of the article.
When MyLogger is loaded, it calls getMessages, which parses the XML
message file and builds a HashMap in Messages. The objects in the HashMap are instances of the Message class, which is a simple bean that holds data for one message. As you can see from the code above, both Message and Messages are specified in the setup to Digester.
When MyLogger needs a message from the HashMap, it calls the getMessage method in the Messages class:
public Message getMessage(String key) {
Message message = (Message) messages.get(key);
if (message == null) {
message = new Message(key, "ERROR", "Missing message for key="
+ key + ". Severity set to ERROR");
}
return message;
}
Should it happen that a key cannot be found in the HashMap, a new Message object is created with severity "ERROR," and some explanatory text (as shown above). A better solution might be to have a default message specified in the XML file as well. Be careful to also handle the case where this default message is absent in the file!
To use the logging system, you must first create an instance of MyLogger. This instance can be kept as a static object, in the same way that you use Log4J or the JDK Logging API. Here's a simplified example:
private static MyLogger logger = MyLogger.getLogger(UseMyLogger.class.getName());
. . .
public static void main(String[] args) {
logger.log("TestINFO");
logger.log("TestWARNING");
logger.log("TestERROR", "some data");
}
The getLogger method works like this:
public static MyLogger getLogger(String className) {
MyLogger myLogger = new MyLogger();
// Insert a JDK logger in the class
myLogger.logger = Logger.getLogger(className);
return myLogger;
}
The only thing left now is to code the log method. Here's a general method that accepts an array with parameters:
public void log(String key, String[] parameters) {
// Get the message for the map
Message message = messages.getMessage(key);
// Get message text
String text = message.getText();
// Any substitution?
if (parameters != null && parameters.length > 0)
text = MessageFormat.format(text, parameters);
// Get severity
String severity = message.getSeverity();
if (severity.equals(Message.INFO))
logger.info(text);
else if (severity.equals(Message.WARNING))
logger.warning(text);
else
logger.severe(text);
}
The message substitution is very simple since you can use Java's MessageFormat API to do the work.
Make sure to include code to handle a case where the message severity is spelled incorrectly in the message file. The most straightforward would probably be to add a DTD or a style sheet to the XML file and have it validated during parsing, but I have not succeeded in doing so. If any of you have a suggestion for how to do this I'd be happy to receive an e-mail.
Working Efficiently
It’s important to any project for developers to work efficiently. One way of achieving this is to document and standardize the tasks involved in every application: logging, error handling, testing, etc. By doing so, developers can concentrate on working out the business tasks, which should always be a developer's main focus.
Happy coding!
Other Resources
New on the Java Boutique:
New Review:
Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling
API boasts simplicity, ease-of-integration, a well-rounded feature
set, and it's free!
New Applet:
Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA
sequences into three useful formats.
Elsewhere on internet.com:
WebDeveloper Java
Lots of Java information on webdeveloper.com
WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.
ScriptSearch Java
Hundreds of free Java code files to download.
jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.
|