Software Reality
Programming with
a dose of satire.

Site Map Search


Agile Development
 
Extreme Programming
 
Code Generation


Articles
Lifecycle
Design
Programming
Soapbox
Reviews
Cthulhu

Java Swing
Swing's greatest threat isn't SWT, it's Flash
Swing Survival Guide


 
Check out our ageing Reviews Section


Use Case Driven
Use Case Driven Object Modeling with UML: Theory and Practice
Get from use cases to working, maintainable source code. Examples use Spring Framework, JUnit and Enterprise Architect

Agile UML
Agile Development with ICONIX Process
A practical subset of agile development techniques, illustrated by example

Get Controversial!
Extreme Programming Refactored
Extreme Programming with a dose of satire
Available now:



Programming

Exception Handling - The REPAIR Model

By Robin Sharp
November 10, 2002

Introduction
Why Exceptions?
The REPAIR Model
R is for Runtime
E is for Exception
P is for Propagation
A is for Atomic
I is for Isolation
R is for Resumption
Summary
References
Dohh!!
Accidents will happen...

Introduction

"It's best to ensure you don't have to repair it at all"

They say "If it ain't broke, don't fix it". But when you are referring to a running computer system, it can be very hard to repair it. In fact it's best to ensure you don't have to repair it at all, and as a last resort ensure that you design reparation into the system.

This paper introduces the Exception Handling REPAIR model. The REPAIR model describes levels of Exception handling in software, and provides a roadmap for developers and QA to measure the robustness of their software. This model fills a gap in the literature between basic tutorials on Exception Handling, advanced Exception Handling published by Journals and practical common designs used to overcome software faults.

Before we start, it's worth noting that this model is not contentious. The levels in the REPAIR model seem to be natural, where each level forms a base for the next level up. The levels are relatively simple and it should be straightforward to implement. Many programmers will be familiar with all the concepts in this document. It's nice however to have these concepts written down and tied together into a coherent pattern. The REPAIR levels are not mandatory, indeed depending on your requirements you may have no Exception Handling requirements at all.

If you want an introduction to the basic mechanics of Exception Handling in Java read the Sun Java Tutorial, which covers the following:

· What is an Exception?
· try, catch, finally statements.
· Catching and Throwing Exceptions.
· User Defined Exceptions.
· Runtime Exceptions

While the Sun Tutorial is a good introduction, it does not cover more advanced Exception Handling designs. In this age of Design Patterns it is worth reflecting that the most important design principle is to understand when to apply them. A problem is that explicit Exception Handling strategies are very rare in the design pattern literature. When somebody asks you about how much Exception Handling you put in your code your mind jumps between counting the try catch blocks to a theoretical 24x7 resilient system with no concrete designs in between. Your consciousness 'heals' over the gap, forming a blind spot in your mental field. The REPAIR model provides a design bridge between these two perceptions, which whilst not providing all the answers provides the basis for them.

 

Why Exceptions?

For some reason, Exception Handling is often not considered high priority when building systems. This is because architectures can be viewed as having 2 sides - Positive and Negative. The positive side includes algorithms and functions. The negative side includes Exception Handling. Perhaps it is because Exception Handling does not deliver tangible functionality to users so they either do not specify it in their requirements, or if they are enlightened ask for 24x7 without realising the cost. One of the problems is that there is no common correli in the real world for exceptions and therefore no real language for users to describe Exception Handling requirements. If you accept that users are not going to lay down clear failure handling strategies and that failure conditions require handling, then designers should take the responsibility for adding these requirements into the specification.

My first lesson in computer science was the principle of GIGO (Garbage In Garbage Out). But that's wrong with that? Actually the preferred principle should be GIEO (Garbage In Exception Out). Perhaps the acronym isn't as 'catchy' as GIGO. Seriously, computer science teaches you algorithms. Programs are not algorithms but systems. Systems fail, algorithms do not. That's what GIGO is all about. As an engineer you should be expecting failure, and designing for it. Applications, machines and networks all fail. When programming you should have your engineer's hat on, not be wearing your rose tinted maths spectacles.

Depending on the level of resilience you require, Exception Handling can provide you with the following:

· Halt Erroneous code.
· Handle Exceptions Separately.
· Make the code more stable.
· Manage resources.
· Allow better Diagnostics.
· Enable recovery Algorithms.

In most systems I've had to audit, Exception Handling has to one extent or another been refactored posthumously into the code. Exception Handling code can often be peeled away in an archaeological fashion. Importantly a system that is not designed from the ground up for REPAIR will always struggle to gain degrees Exception Handling. The higher the levels of REPAIR the harder the struggle. Once you've read the article you'll be able to look back with a better understanding of why this is so.

 

The R.E.P.A.I.R Model

The Exception Handling REPAIR model describes a series of levels that can be designed into your architecture. An application can be assigned a REPAIR level. The higher your application is in the REPAIR model, the higher the level of Exception Handling. The lowest level in the model describes an application with no Exception Handling. The highest level in the model describes an application where repair is possible. This kind of repair is sometimes known as resumption.

Traditionally there are 2 models in Exception Handing, Termination and Resumption. Termination refers to the idea that on failure, the code terminates. Resumption refers to the idea that the code tries to resume from the point where the exception was thrown. The book "The Design and Evolution of C++" (Bjarne Stroustrup) quotes Jim Mitchell saying:

"Termination is preferred over resumption; this is not a matter of opinion but a matter of years of experience. Resumption is seductive, but not valid".

In fact there are very few cases where resumption is required, outside a 24x7 system. Stroustrup mentions that after ten years of use there was only one use of resumption left in the half million lines of Cedar/Mesa code.

The reason problems with resumption have been mentioned is that if you have heard of resumption you've probably heard it was rejected as a pure model, in which case it's important to counter this position by saying that there is a case for resumption semantics, but not a generic one. For example, retry or fail over resumption is valid, but only in the right circumstances. Resumption semantic must be custom. If you haven't heard of resumption, it's important to mention it to stop you getting 'seduced' by the idea, and thinking that resumption semantics for all exceptions would be necessary.

Planning your exception handling strategy involves deciding what level of REPAIR you require. These are the levels of REPAIR:

Level 0 : R untime
Level 1 : E xception
Level 2 : P ropagation
Level 3 : A tomic
Level 4: I solation
Level 5 : R esumption

To illustrate the levels of repair, a hypothetical example will be used that sets a couple of properties on a Bean. This example could typically be part of a Web application form where values are passed in and out as Strings. The example has been kept simple for the sake of brevity, but its simplicity also illustrates the overhead involved in Exception Handling.

The example given is based on the Beans model to ensure clear programming semantics. Without clear programming semantics it is extremely problematic trying to implement robust Exception Handling. This is not for any theoretical reason but the practical difficulties of managing the state of an irregular set of objects.

 

R is for Runtime

The simplest Exception handling strategy is to rely on Runtime Exceptions to report abnormal conditions. Runtime exceptions are unchecked exceptions, which means that methods do not have to declare the Exceptions that they throw, nor do the calling methods have to catch them. This makes life easier. The cost of this is that there is no runtime checking on whether an Exception is thrown or not. The underlying code may stop throwing exceptions and the compiler will not recognise it.

Throwing Runtime Exceptions is claimed to be controversial in the Sun tutorial. However, if it is a requirement that your code can or should exit at any time without any negative effects then Level 0 - RunTime Exceptions may be sufficient to meet your objectives.

In the example the Integer.parseInt method throws a NumberFormatException or a NullPointerException depending if the String is not a valid number, or is null. Both the NumberFormatException and the NullPointerException extend the RuntimeException so the two exceptions are not required to be declared.

The code fragment below illustrates a Level 0 Exception Handling policy.

public class WebForm
{
    protected int value;
    public void setValue( String string ) { value = Integer.parseInt( string ); }
    public int getValue() { return value; }
    public static void main( String[] args )
    {
        WebForm webForm = new WebForm();
        webform.setValue( "1" );
    }
}

 

E is for Exception

The first level of REPAIR is that Exceptions are forced to be considered for Exception Handling. Often the first level of repair simply involves identifying Exceptions on a needs basis, rather than creating a coherent Exception object model.

In our example, the setter method now throws a NumberParseException. NumberParseException is not a RuntimeException and must be caught my the calling method. In the example below we are forced to catch the NumberParseException, and therefore forced to consider handling it. Fundementally this is different from throwing a RuntimeException, where Exception Handling is optional. Not handling the NumberParseException is now a sin of commission, whilst not handling the RuntimeException was a sin of omission.

In some circumstances it may be better to throw RuntimeExceptions, particularly where the code or users are not required to handle exceptions. The Exception reporting has also improved, because a custom NumberParseException is now being thrown and the message is more specific. This message could be customized with localised messages. For a system to achieve Exception level programmers should be forced to consider handling exceptions.

Our example now looks as follows:

public class NumberParseException extends Exception
{
    public NumberParseException( String msg )
    {
        super( msg );
    }
}

public class WebForm
{
    protected int value;

    public void setValue( String string ) throws NumberParseException
    {
        try
        {
            value = Integer.parseInt( string );
        }
        catch( NullPointerException npe )
        {
            throw new NumberParseException( "Cannot set value as the argument is null" );
        }
        catch( NumberFormatException nfe )
        {
            throw new NumberParseException( "Cannot parse the value " + nfe.getMessage() );
        }
    }

    public int getValue() { return value; }

    public static void main( String[] args )
    {
        WebForm webForm = new WebForm();
        try
        {
            webform.setValue( "1" );
            System.out.println( "Value=" + webForm.getValueAsText() );
        }
        catch( NumberParseException npe )
        {
            System.out.println( npe.getMessage() );
        }
    }
}

 

P is for Propagation

Propagation refers to the idea that Exceptions are passed up through the stack to be handled at the required level. Propagation requires that when the Exception is handled, it must contain sufficient information to handle the Exception correctly. In our current example, it may be a requirement that the user is informed of the specific field that can't be parsed. This will then enable the exception handling code to highlight the error on the user interface. In our example we replace the NumberFormatException with a ValidationException. To satisfy the field-naming requirement the ValidationException can be appended to include the name of the field that causes the Exception.

In large-scale architectures with long lifetimes, it is often important to observe architecture neutrality, particularly in relation to persistence, user interface and communications. In your classic 'layered' architecture, the business logic acts as the meat between the User Interface and Persistence layers. A well-designed architecture will hide the specific persistence implementation (e.g. a File or Database). The effect of this design is that exceptions must also hide the implementation (e.g IOException or SQLException). The exceptions must be designed to be architecture neutral.

For exceptions to be architecture neutral, an architecture neutral exception hierarchy must be designed. In the case of persistence this model should include CRUD (create, retrieve, update and delete) exceptions, where these exception generalise and encapsulate implementation specific exceptions (e.g. IOExceptions, EJBExceptions or SQLExceptions). In the case of our example, the NumberParseException will be expressed generically as a ValidationException.

In a 'Bean' Architecture the following architecture Neutral exception hierarchy could be designed:

Exception
   |__ BeanException
           |__ CreateException
           |__ StoreException
           |__ FinderException
           |__ RemoveException
           |__ ValidationException

In order to preserve fidelity (at least for logging purposes), these Architecture Neutral Exceptions should allow the exception that caused the originally error to be encapsulated. A standard design that allows Exceptions to encapsulate other exceptions is called the Chained Exception. JDK 1.4 now supports Chained (or Nested) Exceptions. The Throwable class, now has a property called getCause() . If you have to support pre JDK1.4 code then it's easy enough to copy this property in your own root Exception class. The getCause() property allows an exception to be passed into the constructor of another Exception to indicate what caused that exception in the first place. Loggers or diagnostic code introspect the outermost Exception for its underlying cause.

Applying this example to JSPs, you can define an error handler for a page, i.e. if you throw an exception and don't specifically handle it in the page, then it gets handed over to an exception page which is displayed to the user. In reality, this error page should normally never be seen or called, but it is useful to have a sane error page set up for when your code eventually blows up in some unexpected fashion. Better to have a good stack trace than "Woops, I've gone wrong". This isn't so much about handling the exception, as at this stage it's too late to catch/rethrow etc, but more about how you log and display your exceptions.

Our example now looks as follows:

public class ValidationException extends Exception
{
    protected Object object;
    protected String property;
    
    public ValidationException( Object object, String property, String msg, Throwable cause )
    {
        super( msg, cause );
        this.object = object;
        this.property = property;
    }

    public Object getObject() { return object; }
    public String getProperty() { return property; }
}

public class WebForm
{
    protected int value;

    public void setValue( String string ) throws ValidationException
    {
        try
        {
            value = Integer.parseInt( string );
        }
        catch( NullPointerException npe )
        {
            throw new ValidationException( this, "value", "Cannot set value as the argument is null", npe );
        }
        catch( NumberFormatException nfe )
        {
            throw ValidationException( this, "value", "Cannot parse the value ", nfe );
        }
    }

    public int getValue() { return value; }

    public static void main( String[] args )
    {
        WebForm webForm = new WebForm();
        try
        {
            webform.setValue( "1" );
            System.out.println( "Value=" + webForm.getValueAsText() );
        }
        catch( ValidationException ve )
        {
            System.out.println( "Cannot validate " + ve.getProperty() );
        }
    }
}

 

A is for Atomic

The next 3 levels of Exception handling focus less on the Exception and more on the resources that are being handled.

Before we begin, it's worth noting an important design principle required in Exception Handling: performance asymmetry. This principle simply states that the performance of Exception Handling should not compromise the performance of trying the operation in the first place. The reason for this is that Exception Handling takes place under abnormal conditions, and it shouldn't affect the system's normal performance.

There are several strategies in code for enabling atomicity. Depending on your requirements, these strategies vary in sophistication. The first place to start examining atomicity is its origins as one of the ACID properties of databases (Atomicity, Consistency, Isolation and Durability).

The Atomic property in databases is that an action either happens or doesn't happen. When an Exception is thrown, several operations may have been performed since the start of the try block. In order for exception handling to reach the atomic level, these changes should (if demanded) be reversed. The property of Atomicity demands that the state of the resources being managed by the exception block can be returned to the same state before the exception block began. This means that operations must either be commutable (reversible) or the state of the resources can be reloaded.

There are several coding strategies to enable atomicity. The Atomic property of database atomicity is enabled using Transactions. One of the main functions of Transactions is to provide a means to tell the application when a change in state of the system has begun, when the change in state finished, and to undo those changes if an error occurs. The transaction boundaries of begin, commit and rollback correspond closely to the start try, end try and catch statements.

We can extend our example to illustrate how a Bean might enable transaction handling using the concept of a BeanUserTransaction to manage the state (beans properties) in the application. The javax.transaction package includes an interface for a UserTransaction. This interface includes begin, commit and rollback methods, and can be used as an interface for the BeanUserTransaction. The code fragment below illustrates this interface.

public interface UserTransaction
{
    void begin();
    void commit();
    int getStatus();
    void rollback();
    void setRollbackOnly();
    void setTransactionTimeout(int seconds);
}

We can implement a version of this class that can listen for changes to Beans using the PropertyChangeListener, and 'remember' the PropertyChangeEvents on the Bean. Because the PropertyChangeEvents include the old property value then if an Exception is thrown, and the changes are rolled back, then the old values can be re-applied to the Bean in reverse chronological order using introspection; assuming the properties are commutative. It is a requirement of this design that properties must be 'bound' (throw PropertyChangeEvents) if they are to be rolled back.

The code fragment below illustrates the class for the BeanUserTransaction:

public class BeanUserTransaction implements UserTransaction, PropertyChangeListener
{
    protected List beans = new ArrayList();
    protected List events = new ArrayList();

    public Object addBean( Object bean )
    {
        MethodUtil.invoke( bean, "addPropertyChangeListener", PropertyChangeListener.class, this );
        beans.add( bean );
    }

    public Object removeBean( Object bean )
    {
        beans.remove( bean );
        MethodUtil.invoke( bean, "removePropertyChangeListener", PropertyChangeListener.class, this );
    }

    public void rollback()
    {
        for( int index = events.size()-1; index >=0; index++ )
        {
            unexecute( (PropertyChangeEvent)events.get(index) ):
        }
        events.clear();
        beans.clear();
    }

    public void propertyChange(PropertyChangeEvent pce)
    {
        events.add( pce );
    }

    . . .
}

Another option that could be used to implement this interface is to clone the Beans that are added to the BeanUserTransaction. Depending on how pessimistic you expect the number of property changes to be in the transaction, cloning may be a more efficient means to keep track of property changes. In an optimistic model you expect very few changes to be made to the resources - in which case it would be more efficient to listen to property change events. In a pessimistic model you expect a lot of changes to be made to the resources - in which case it would be more efficient to clone the objects.

If we extend our example WebForm bean to include several properties, we can then focus on the Exception Handling code. The code fragment below illustrates the properties being managed by a BeanUserTransaction:

public class WebForm
{
    protected String value;
    protected int number;
    public void setValue( String string ) throws ValidationException { ... }
    public String getValue() { ... }
    public void setNumber( int number ) throws ValidationException { ... }
    public int getNumber() { ... }

    public static void main( String[] args )
    {
        WebForm webForm = new WebForm();
        BeanUserTransaction transaction = new BeanUserTransaction();
        try
        {
            transaction.begin();
            transaction.addBean( webForm );

            webform.setValue( "1" );
            webform.setNumber( 2 );

            transaction.commit();
        }
        catch( Exception e )
        {
            transaction.rollback();
        }
        finally
        {
            //tidy up resources
            transaction.removeBean( webForm );
        }
    }
}

Where a persistence store is being used, Bean Transactions can be used in conjunction with persistence Transactions (such as JDBC) to ensure that the database and the code stay in the same state. Another way to keep beans in the same state as the persistence store is to reload the Bean on the event of Transaction failure. For Bean reloading to be used it is a requirement that the state of the Bean at the start of the try block is the same as the state of the Bean in the persistence store - which may not be the case.

 

I is for Isolation

Isolation refers to the property that changes made to the resources are made in isolation to other processes. This means that whilst a resource is being changed, other processes cannot view those changes.

The simplest form of isolation is to ensure that each process has its own copy of the data. In most applications this is unfeasible, so a more sophisticated mechanism is needed.

Isolation also has its origins as one of the ACID properties of databases. The Isolation property in databases is that while a transaction is in progress, the changes to the tables are not visible to other processes until the transaction is committed.

Isolation in databases, and for our purposes, is implemented using a Lock Manager. Lock Managers provide a degree of control over access to an Object, whether it is to read or write to that object. Lock Managers have a close relationship to Transactions in that objects can be locked at the start or end of a transaction, depending on how optimistic you wish your locking to be. Optimistic locking is optimistic because it presumes that the resources will not be changed during the lifetime of the transaction.

There are currently no LockManager classes, or interfaces, in the Java API's. The new java.util.concurrency package (JSR 166) includes a Lock and Locks class, but this will be to determine whether a process can run. For our purposes the LockManager class has the following interface:

public abstract LockManager
{
    public static LockManager getLockManager();
    public static LockManager getLockManager( Object context );
    public abstract List lock( List locks ) throws LockException;
    public abstract List lock( List locks, long timeout ) throws LockException;
    public abstract Object lock( Object lock ) throws LockException;
    public abstract Object lock( Object lock, long timeout ) throws LockException;
    public abstract List release( List locks );
    public abstract Object release( Object lock );
}

Note : The Lock Manager returns the Object and List locked in case some kind of cloning of the Object is required by the locking implementation.

In the code fragment below, the System LockManager is retrived from the factory method and used to lock the resource involved in the transaction. Incidentally, the LockManager could have been encapsulated inside the BeanUserTransaction. If it was, it would mean that when beans are added to the BeanUserTransaction, they are locked either at the start or the end of the transaction - dependning on how optimistic you want your locking to be. For this example the LockManager has been exposed. If a bean cannot be locked then a LockException is thrown that will rollback the transaction:

public class WebForm
{
    protected String value;
    protected int number;
    public void setValue( String string ) throws ValidationException { ... }
    public String getValue() { ... }
    public void setNumber( int number ) throws ValidationException { ... }
    public int getNumber() { ... }

    public static void main( String[] args )
    {
        WebForm webForm = new WebForm();
        LockManager lockManager = LockManager.getLockManager();
        BeanUserTransaction transaction = new BeanUserTransaction();

        try
        {
            webForm = lockManager.lock( webForm );

            transaction.begin();
            transaction.addBean( webForm );

            webform.setValue( "1" );
            webform.setNumber( 2 );

            transaction.commit();
        }
        catch( Exception e )
        {
            transaction.rollback();
        }
        finally
        {
            webForm = lockManager.release( webForm );
        }
    }
}

Locking is a fairly drastic way to isolate an Object. This means that if somebody is reading an Object in a list then nobody else can read or write to it. When you think that most applications search for data and display lists of objects, then the impact may become untenable.

An improvement in this design would be to allow the Lock Manager to distinguish between read and write intents. Read intent means that the Lock Manager would not lock the object, but register that there was an intention to read that Object. When an object was registered with read intent, an increment count could be kept and when it was released an increment count could be decremented.

In this way the Lock Manager would know how many read intents were outstanding. Write intents would register intent to write to the object. That object could be locked and further read intents could be stopped, but new write intents would not be allowed. In this way the process with read intent could either block or be aware if an object was about the change underneath them.

The locking strategies discussed so far suffer from degraded liveness, which is that all the data may not be available all the time. A design option that improves liveness is that when an Object is registered with write intent, it could be cloned either by the Lock Manager or by the application. For this strategy to work successfully, objects that are registered for write intent accept a contract that they cannot be referenced externally. When these objects are committed to the database, the Lock Manager must merge the changes back into the original objects once all the read intents have been released.

 

R is for Resumption

Historically there have been two conflicting goals in Exception Handling: termination and resumption. Termination refers to the goal that when an Exception is thrown, the code terminates. This is what we have been dealing with so far. Resumption offers a more exotic option, that the code can somehow heal itself after an abnormal condition.

Resumption offers the highest level of Exception Handling, but it comes at the cost of having to implement all the strategies discussed so far.

Resumption semantics have traditionally been implemented using various strategies such as retries, failover and data cleansing, all of which depend on the type of the abnormal condition. Implementing resumption semantics requires that the operations can be repeated. In our example, the statements themselves have already been captured using the BeanUserTransaction. The addition of a retry method allows us retry the statements, by replaying the PropertyChangeEvents in chronological order.

The code fragment below illustrates how resumption semantics could be implemented in the BeanUserTransaction and an ExceptionHandler interface:

public interface ExceptionHandler
{
    /**
    * Possibly modify the PropertyChangeEvents given the exception before trying.
    */
    List public( List propertyChangeEvents, Throwable throwable );
}


public class BeanUserTransaction implements UserTransaction, PropertyChangeListener
{

    . . .

    Map handlers = new HashMap();

    public void addExceptionHandler( Class exceptionClass, ExceptionHandler handler )
    {
        handlers.put( exceptionClass, handler );
    }

    public void removeExceptionHandler( Class exceptionClass )
    {
        handlers.remove( exceptionClass );
    }

    public void retry( Throwable throwable ) throws Throwable
    {
        ExceptionHandler handler = (ExceptionHandler)handlers.get( throwable.getClass() );
        if( handler != null )
        {
            events = handler.handle( events, throwable );
        }
        else
        {
            throw throwable;
        }

        try
        {
            begin();

            for( int index = 0; index < events.size(); index++ )
            {
                execute( (PropertyChangeEvent)events.get(index) ):
            }

            commit();
        }
        catch( Throwable t )
        {
            rollback();
            retry( t );
        }
    }
}

In our example, we now presume that the LockManager is encapsulated inside the BeanUserTransaction. A RetryHandler is set on the BeanUserTransaction if a ValidationException is thrown:

public class WebForm
{
    protected String value;
    protected int number;
    public void setValue( String string ) throws ValidationException { ... }
    public String getValue() { ... }
    public void setNumber( int number ) throws ValidationException { ... }
    public int getNumber() { ... }

    public static void main( String[] args )
    {
        WebForm webForm = new WebForm();
        BeanUserTransaction transaction = new BeanUserTransaction();
        Transaction.addHandler( ValidationException.class, new RetryHandler(context) );
        try
        {
            transaction.begin();
            transaction.addBean( webForm );

            webform.setValue( "1" );
            webform.setNumber( 2 );

            transaction.commit();
        }
        catch( Exception e )
        {
            transaction.rollback();
            transaction.retry( e );
        }
        finally
        {
            //tidy up resources
            transaction.removeBean( webForm );
        }
    }
}

 


Summary

Ten years ago, somebody asked me how many lines of code I would place my life on. I said 10. They told me the highest answer anybody had given them was 14. My answer now would be "Can I code generate the code and what level of exception handling am I allowed to engineer" - the rationale being that code generation reduces the level of human error and increases the level of type checking and regularity. A high level of Exception Handling can leverage these two qualities and reduce the level of runtime errors to make the code more robust.

Finally, this article is not intended to be prescriptive but an example of how levels of Exception Handling strategies, necessarily, build upon each other to create a more robust architecture.

 

References

http://java.sun.com/docs/books/tutorial/essential/exceptions/index.html
http://www.artima.com/designtechniques/desexcept.html
http://www.artima.com/intv/distribP.html
http://java.sun.com/products/jta/
http://gee.cs.oswego.edu/dl/concurrent/index.html

About the author:
Robin Sharp is the Managing Director of Javelin Software (http://www.javelinsoft.com).
He can be reached at

 

Talkback:

Post a new message

Message Index:

FYI
Matt mattc_26@yahoo.com

RetryHandler(context) ... where/how is this defined ?  Thanks ...
HS Thomas

Recusive Hanlding
Robin robin.sharp@javelinsoft.com

The Messages:
FYI
In the last section "Resumption" there are what appear to be a couple of errors.

The ExceptionHandler Interface method is written as list public(...); I think you meant to put handle here as indictated in the code following it's definition.

Second the recursive retry(...) method doesn't have an explicit termination cause. Yes, the handle method of the exception handler can and may very well alter the data in an appropriate way to allow the transaction to proceed if in fact the data is at fault here. If the exception in question is more perverse then I you'll have a problem.

Matt mattc_26@yahoo.com
Boston, USA

Mon Nov 25 20:09:11 GMT 2002
RetryHandler(context) ... where/how is this defined ?
Thanks

HS Thomas

Tue Nov 26 09:20:51 GMT 2002
Recusive Hanlding
The retry method's recursive termination is handled by there not being a handlers for the throwable argument. At the start of the first/nth recusive retry() call, the retry() method tries to find a handler for the throwable argument, if one does not exist the Throwable that was passed into the method is thrown back out of the method.

As the retry() method was the last thing called in it's parents catch block the Throwable that was passed in is thrown back out again and up to its parents retry() method and so on, until it is thrown out of the last method on the recursive stack.

The RetryHandler context - is supposed to be an example of how you can pass your current context (whatever that is) into a handler so that it is aware of the current environment. It symbolises client code, rather than referring to anything specific.

Hope this clarifies some issues.

Robin robin.sharp@javelinsoft.com
London, England

Tue Nov 26 14:09:06 GMT 2002

Post a new message

 

Related Articles:

Improve Your Interface Designs

Code Generators - The Fastest Way to Write Software?

Why 'JavaServer Faces Early Access 1.0' Falls Short of the Mark

<< Back to Programming

All trademarks and copyrights on this page are owned by their respective owners.
Stories and articles are owned by the original author.
All the rest Copyright © 1998-2008 Matt Stephens. ALL RIGHTS RESERVED.