|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 2
Having said that, if the normal behavior is compiler-enforced, how did I create EvilFoo? There are at least two ways you can create Java methods that throw checked exceptions they never declared:
Thread.stop(Throwable) has been deprecated for some time, but it still works and will inject an arbitrary Throwable into whichever Thread it is called onEvilFoo against a temporary version of IFoo that actually declares bar() to throw whichever checked exception type you fancy
I used a variation of the latter option: I compiled a class EvilThrow initially defined as:
public abstract class EvilThrow
{
public static void throwThrowable (Throwable throwable)
throws Throwable
{
throw throwable;
}
}
JasminVisitor from the Byte Code Engineering Library (BCEL), removed the throws Throwable declaration from the throwThrowable() method in the assembly code, and compiled the new version using Jasmin assembler (see Resources for links to both tools). The resulting class is available for your exception throwing pleasure in this article's download.This brings me to the following maxim: if you have a valid reason to code a catch-all construct, it should always catch java.lang.Throwable and not merely java.lang.Exception. It applies whenever you develop a managed runtime application (such as a component container) and must execute external
components (i.e., servlets, Enterprise JavaBeans (EJB), and JavaBeans) that might contain buggy or even malicious code. Make
sure you catch Throwable and filter out Errors as needed.
The following example illustrates what can happen if you do not follow this advice.
javax.swing.SwingUtilities.invokeAndWait() is a popular utility method for executing a Runnable on the Abstract Windowing Toolkit (AWT) event thread. It is used when an application thread must update the graphical user
interface (GUI) and obey all Swing threading rules. The documentation states that an uncaught exception from the Runnable.run() will be caught and rethrown, wrapped in an InvocationTargetException.
The implementation of this in Sun Microsystems' Java 2 Platform, Standard Edition (J2SE) 1.4.1 assumes, somewhat naively,
that such uncaught exceptions can only be subclasses of java.lang.Exception. Here is an extract from java.awt.event.InvocationEvent used by SwingUtilities.invokeAndWait() behind the scenes:
public void dispatch() {
if (catchExceptions) {
try {
runnable.run();
}
catch (Exception e) {
exception = e;
}
}
else {
runnable.run();
}
if (notifier != null) {
synchronized (notifier) {
notifier.notifyAll();
}
}
}
The problem with this code is that if runnable.run() throws a Throwable, the catch block is not entered, and the notifier.notifyAll() line never executes. The calling application thread will then wait on a nonpublic lock object inside java.awt.EventQueue.invokeAndWait() that will never be signaled (lock.wait() will never execute):
public static void invokeAndWait(Runnable runnable)
throws InterruptedException, InvocationTargetException {
class AWTInvocationLock {}
Object lock = new AWTInvocationLock();
InvocationEvent event =
new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
true);
synchronized (lock) {
Toolkit.getEventQueue().postEvent(event);
lock.wait();
}
Exception eventException = event.getException();
if (eventException != null) {
throw new InvocationTargetException(eventException);
}
}
To see the resulting deadlock for yourself, make EvilFoo implement Runnable:
public void run ()
{
bar ();
}
Main as follows:
SwingUtilities.invokeAndWait (new EvilFoo (new Throwable ("SURPRISE!")));
As you can see, failure to be defensive about exceptions can make untrusted code force your code into execution paths you may not be prepared to handle.
I hope this article stimulated your thinking about what it takes to write truly defensive Java code and provided a new perspective
on Java exceptions. I doubt the EvilThrow class will be useful in a real application, but you might find it handy for testing exception safety and defensibility of
your designs.
Read more about Core Java in JavaWorld's Core Java section.