Hip Wrappers in the House

In March 2002, Sun Microsystems released Version 1.4 of its Java 2 Standard Edition (J2SE) software (http://java.sun.com/j2se/1.4). J2SE is the core of the Java platform. While other Java kits are specialized (e.g., J2ME is targeted at small devices and J2EE provides a range of server and e-commerce solutions), J2SE provides fundamental components that every Java programmer can use.

In March 2002, Sun Microsystems released Version 1.4 of its Java 2 Standard Edition (J2SE) software (http://java.sun.com/j2se/1.4). J2SE is the core of the Java platform. While other Java kits are specialized (e.g., J2ME is targeted at small devices and J2EE provides a range of server and e-commerce solutions), J2SE provides fundamental components that every Java programmer can use.

For example, the Java Container framework (Maps, ArrayLists, etc.), the input/output system, and Swing and AWT (two user interface toolkits for Java written and provided by Sun) are all a part of J2SE.

J2SE 1.4 is a substantial upgrade with more than thirty enhancements or additions — new APIs, development tools, and performance tweaks — that greatly extend Java’s capabilities. The additions to J2SE 1.4 fall into four categories: the integration of previously supplemental APIs into the core Java 2 platform; new APIs; new Java facilities (an addition to the Java language or a substantial set of new methods added to existing classes); and enhancements and bug fixes.

Here are some highlights from each area.

INTEGRATIONS AND NEW APIs



  • The Java API for XML (JAXP) and several Java security packages are now integrated in J2SE as part of the core platform.

  • Applications can use the new Java Logging API to generate event and activity logs. Logs can be emitted as plain text, XML, or can be sent to system log services such as syslog. Logs can also be captured in more than one place simultaneously.

  • The Java New I/O API improves performance and provides for memory mapping. New I/O also includes extensive Perl-like pattern matching.

NEW JAVA FACILITIES



  • The Java language now includes an assert facility that can help find bugs in your code, and lets you selectively enable or disable assertions at compile- or run-time.

  • The fundamental exception class, Throwable, is now able to chain exceptions (more on this in a bit).

  • The Collection framework has been extended. One specific new class, LinkedHashMap, provides an insertion-ordered Map that runs nearly as fast as HashMap.

ENHANCEMENTS AND BUG FIXES



  • Java2D supports hardware acceleration for offscreen images.

  • The updated Swing user interface toolkit can swap data using drag-and-drop.

  • JDBC 3.0 can keep result sets open after a transaction is committed.

To see a complete list of changes in J2SE 1.4 see Sun’s official release notes at http://java.sun.com/j2se/1.4/relnotes.html.

This month we’ll examine a J2SE 1.4 feature that all Java programmers can benefit from: the new “chained exceptions” mechanism. Upcoming columns will take a look at Java’s new Perl-like regular expressions, the new Java Logging API, and the New I/O (NIO) system.

All of the examples in this month’s column were created with J2SE 1.4 using Forte for Java 3.0. You can download J2SE 1.4 and Forte 3.0 Community Edition for free from http://www.java.sun.com. Software for Linux, Solaris, and Windows is available; Red Hat Linux users can install from an RPM. (If you currently create Java applications with Forte 2.0 or NetBeans 3.2 and want to develop with J2SE 1.4, you’ll have to download and use Forte 3.0.)

On the Chained Gang

If you’ve ever written a Java class library, it’s likely that you created your own set of exceptions to show the different types of errors that could occur with your API. In general, this is good design: your exceptions are consistent with the higher-level abstraction presented by your API.

For example, the Java fragment below anticipates an exception called SomeException, catches it, and throws MyException. SomeException could be a standard Java exception or a custom exception.


try {

} catch (SomeException e) {
throw new MyException();
}

This kind of code is very common. It’s often used to convert an internal or standard exception to an exception more appropriate to the API being used. The intent is to hide low-level implementation details and provide more information by throwing an exception that better reflects the error that occurred.

Unfortunately, catching one kind of exception and throwing another often hides the root cause of a problem. Worse, exception chaining (what we’ve implemented above) destroys valuable information, such as the stack backtrace.

For example, if you try to debug the snippet above to find the cause of MyException, chances are you’ll ultimately need to know what caused SomeException. However, once you’ve thrown MyException, the cause and stack trace of SomeException is lost. This implementation, while well-intended, actually hurts you instead of helping.

Old School Wrapping

To work around the “disappearing exceptions” problem, many Java programmers have built their own exception chaining mechanisms called “exception wrappers.” Remember, an exception is just a class. An exception wrapper is simply an exception that contains another exception. Here’s an example of a Java class (written prior to the release of J2SE 1.4) that wraps another exception.

public class MyExceptionWrapper extends
Exception {
private Exception _wrappedException;

public MyExceptionWrapper(Exception e) {
super();
this._wrappedException = e;
}
public Exception getWrappedException() {
return this._wrappedException;
}
}

To use the wrapper, you catch an exception, the causative exception, and embed it in a new MyExceptionWrapper. This is shown below.


try {

} catch (SomeException e) {
throw new MyExceptionWrapper(e);
}

To examine the original exception you use the accessor method getWrappedException() when you catch a MyExceptionWrapper:


try {

} catch (MyExceptionWrapper mew) {
Exception e = mew.getWrappedException();
e.printStackTrace();
}

This implementation of exception chaining is much better than the previous example and can be used to create chains of any length. However, there is one significant disadvantage: you have to create the wrapper class in all of your programs. Moreover, you’ll also have to write code if you want the standard and other Java exceptions wrapped as well.

Hipper Wrapping

Luckily, J2SE 1.4 adds exception chaining to the core platform. The new chained exception facility offers a common API to capture causative exceptions. With this new API you can easily access the causative exception and follow the entire “causal chain” as part of a standard stack backtrace. Best of all, this new facility allows any exception to record the causative exception no matter who wrote the exception class.

To implement chained exceptions, there are two new constructors and two new methods in Throwable, the superclass of all errors and exceptions in Java. (Other “general purpose” Java exceptions like RunTimeException, Exception, and Error also include these two new constructors.)


  • The constructor Throwable(Throwable t) creates a new exception given a causative exception, t. The new exception wraps the causative exception.

  • The constructor Throwable(String s, Throwable t) has the same purpose as the previous constructor, but can also record a String s message in the new exception.

  • The method public Exception getCause() returns the wrapped exception.

  • The method public void initCause(Throwable t) can be used to wrap a causative exception. initCause() (implemented in the Throwable superclass) allows you to wrap exceptions in any exception class even if that class is not chained-exception aware.

Let’s look at how to use the new chained exception facility. Listing One contains an example of how to wrap a causative exception in IllegalArgumentException, an exception that does not (even in J2SE 1.4) have exception chaining constructors.




Listing One: Chained exceptions


package com.linuxmag.javamatters;

public final class JuneExample {
private static int stringToInt(String s) throws
IllegalArgumentException
{
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
throw (IllegalArgumentException) new
IllegalArgumentException().initCause(e);
}
}

public static void main (String args[]) {
for (int i = 0; i < args.length; i++) {
try {
int result = stringToInt(args[i]);
System.out.println(result + ” * ” +
result + ” = ” + (result*result));
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}
}

The code in Listing One computes the square of an integer. Since command-line arguments are passed in as Strings, each argument must be converted to an int before computation can occur.

The conversion from a String to an int is performed by the method stringToInt() and, specifically, by the static method Integer.parseInt(). If argument s does not represent an integer, a NumberFormatException is thrown. NumberFormatException is wrapped and the method throws an IllegalArgumentException instead.

IllegalArgumentException is a legacy exception (not chained-exception aware), so initCause() must wrap the causative exception. Please note that initCause() returns Throwable; the cast to IllegalArgumentException is necessary because the method stringToInt() throws an IllegalArgumentException.

If this Java application is called with arguments 5, 10, hello, the output would look like Figure One .




Figure One: Sample output of exception chaining


5 * 5 = 25
10 * 10 = 100
java.lang.IllegalArgumentException
at com.linuxmag.javamatters.JuneExample.stringToInt(JuneExample.java:17)
at com.linuxmag.javamatters.JuneExample.main(JuneExample.java:24)
Caused by: java.lang.NumberFormatException: hello
at java.lang.Integer.parseInt(Integer.java:426)
at java.lang.Integer.parseInt(Integer.java:476)
at com.linuxmag.javamatters.JuneExample.stringToInt(JuneExample.java:9)

As you can see from the sample output, the implementation of Throwable.printStackTrace() has been modified in J2SE 1.4 to display backtraces for the entire causal chain of an exception. From the stack trace you can clearly find the oroginal cause of IllegalArgumentException.

Now let’s write our own exception, MyException, that uses the new exception chaining mechanisms. See Listing Two. Since the superclass Exception is now chained-exception aware, writing a new exception wrapper requires little coding. Just mimic the constructors of Exception and call super(). To use MyException in the sample application, replace IllegalArgumentException with MyException everywhere in the code.




Listing Two: Code for MyException


public class MyException extends Exception {
public MyException() {
super();
}

public MyException(String s) {
super(s);
}

public MyException(Throwable e) {
super(e);
}

public MyException(String s, Throwable e) {
super(s, e);
}
}

Notice that MyException also includes a constructor with no arguments and a constructor with a single String argument. These constuctors emulate exceptions that are not chained-exception aware.

New Java vs. Old Java

It’s important to note that your existing Java source code and binary class files are upwards-compatible (with a few exceptions) with J2SE 1.4 compilers and virtual machines. If you have source code or class files created with previous releases of the JDK, you will be able to recompile or re-use those assets unchanged (again, with a few exceptions). J2SE 1.4 is not backwards-compatible, but that’s a small price to pay for all of the goodies included in the new release.



Martin Streicher is an Executive Editor at Linux Magazine. He can be reached at mstreicher@linux-mag.com

Fatal error: Call to undefined function aa_author_bios() in /opt/apache/dms/b2b/linux-mag.com/site/www/htdocs/wp-content/themes/linuxmag/single.php on line 62