Ken (Chanoch) Bloom's Blog

2nd March 2009

Error handling/cleanup patterns

I was reading some articles about the use of goto for error cleanup in C code, and started thinking about how one could mimic some of the elegance of goto for error cleanup in the presence of exceptions. Obviously goto itself is out of the question because you don't have a place to do an explicit goto when you encounter an error, and in Java you don't even have a goto statement.

Robert Love (on LKML) shows a very elegant example of cleanup code in C, using goto:

do A
if (error)
    goto out_a;
do B
if (error)
    goto out_b;
do C
if (error)
    goto out_c;
goto out;

out_c:
undo C
out_b:
undo B:
out_a:
undo A

out:
return ret;

In this code, if you have an error after doing a few steps of initialization, you jump to the appropriate place in the reverse-order undo code, and continue from there. The most obvious similar construct in C++ or Java with exception handlers would be to have several levels of nesting:

try{
    do A
    try{
        do B
        try{
            do C
        }catch(Exception){
            undo C
            throw;
        }
    }catch(Exception){
        undo B
        throw;
    }
}catch(Exception){
    undo A
    throw;
}

But a more elegant way to do cleanup would be to take advantage of the fall-through in a switch statement:

int progress=0;
try{
    do A
    progress++;
    do B
    progress++;
    do C
    progress++;
}catch(Exception){
    switch(progress){
    case 3:
        undo C
        //break intentionally omitted
    case 2:
        undo B
    case 1:
        undo A
    case 0:
    }
}

Isn't that more readable?

Permalink | c, c++, java.
20th June 2007

ForcedDTD

import java.io.*;
import java.net.*;
import org.xml.sax.*;
import org.xml.sax.ext.*;

/**
forces an XML parser to use a specified DTD when processing a document.
This is forced even if no !DOCTYPE declaration was given, or 
even if the wrong declaration was given.
*/
public class ForceDTD implements EntityResolver2{
   private Object DTDObject;
   /**
   @param dtdsource Takes a URL, File or String object indicating the 
   location of the DTD to return.
   */
   public ForceDTD(Object dtdsource){
      DTDObject= dtdsource;
   }

   public InputSource getInputSource()
      throws SAXException, IOException{
      if (DTDObject instanceof String)
     return new InputSource((String) DTDObject);
      if (DTDObject instanceof File)
     return new InputSource(new FileReader((File) DTDObject));
      if (DTDObject instanceof URL)
     return new InputSource(((URL) DTDObject).openStream());
      throw new SAXException("Forced DTD must be specified as a String (SystemID), File or URL");
   }

   public InputSource  getExternalSubset(String name, String baseURI) 
      throws SAXException, IOException {
      return getInputSource();
   }
   public  InputSource  resolveEntity(String name, String publicId, 
      String baseURI, String systemId)
      throws SAXException, IOException {
      return getInputSource();
   }
   public InputSource resolveEntity(String publicId, String systemId)
      throws SAXException, IOException{
      return getInputSource();
   }
}
Permalink | java.
21st May 2006

SelfCleaningResultSet

The SelfCleaningResultSet class is a proxy for JDBC that uses delegation to delgate to a ResultSet, but also calls close() on a Statement when the ResultSet is closed.

import java.lang.reflect.*;
import java.sql.ResultSet;
import java.sql.Statement;

public class SelfCleaningResultSet implements java.lang.reflect.InvocationHandler{
   private ResultSet rs;
   private Statement s;
   public static ResultSet wrap(ResultSet rs, Statement s){
      return (ResultSet) Proxy.newProxyInstance(
     rs.getClass().getClassLoader(),
     rs.getClass().getInterfaces(),
     new SelfCleaningResultSet(rs,s));
   }

   private SelfCleaningResultSet(ResultSet rs,Statement s){
      this.rs=rs;
      this.s=s;
   }

   public Object invoke(Object proxy, Method m, Object[] args) throws 
      Throwable{
        Object result;
        try {
            result = m.invoke(rs, args);
        if (m.getName().equals("close"))
           s.close();
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw new RuntimeException("unexpected invocation exception: " +
                                       e.getMessage());
        }
        return result;
   }
}
Permalink | java.
My Website Archives

Tags