Saturday, December 18, 2010

Automatically Retry Failed Jobs in Quartz

How do you handle job failures in Quartz? There are a few things you can do:
  • Do nothing. Let the job fail and log the error.
  • Retry continuously until the job succeeds.
  • Retry n times and then disable the job.
In this post, I will describe how you can configure your jobs to be retried on failure.

Retrying continuously until success:
If you want to keep trying over and over again until the job succeeds, all you have to do is throw a JobExecutionException with a flag to tell the scheduler to fire it again when it fails. The following code shows how:

class MyJob implements Job {

  public MyJob() {
  }

  public void execute(JobExecutionContext context)
                  throws JobExecutionException {
    try{
        //do something
    }
    catch(Exception e){

        Thread.sleep(10000); //sleep for 10 secs

        JobExecutionException e2 = new JobExecutionException(e);
        //fire it again
        e2.refireImmediately();
        throw e2;
    }
  }
}
Retrying n times:
It gets a bit more complicated if you want to retry a certain number of times only. You have to use a StatefulJob and hold a retryCounter in its JobDataMap, which you increment if the job fails. If the counter exceeds the maximum number of retries, then you can disable the job if you wish.
class MyJob implements StatefulJob {

  public MyJob() {
  }

  public void execute(JobExecutionContext context)
                                 throws JobExecutionException {
    JobDataMap dataMap = context.getJobDetail().getJobDataMap();
    int count = dataMap.getIntValue("count");

    // allow 5 retries
    if(count >= 5){
        JobExecutionException e = new JobExecutionException("Retries exceeded");
        //unschedule it so that it doesn't run again
        e.setUnscheduleAllTriggers(true);
        throw e;
    }


    try{
        //do something

        //reset counter back to 0
        dataMap.putAsString("count", 0);
    }
    catch(Exception e){
        count++;
        dataMap.putAsString("count", count);
        JobExecutionException e2 = new JobExecutionException(e);

        Thread.sleep(10000); //sleep for 10 secs

        //fire it again
        e2.refireImmediately();
        throw e2;
    }
  }
}

Saturday, December 11, 2010

Throw a checked exception from a method without declaring it

When you try to throw a checked exeption from a method in Java, the compiler will force you to either catch it or declare it to be thrown in the method declaration. For example, the following method fails to compile:
public void go(){
    throw new IOException();
}
The compiler says:
Dummy.java:15: unreported exception java.io.IOException;
must be caught or declared to be thrown
    throw new IOException();
    ^
1 error
However, if you declare it to be thrown in the method declaration, like this:
public void go() throws IOException{
    throw new IOException();
}
then it compiles successfully.

But is it possible to throw a checked exception WITHOUT declaring it to be thrown? You could wrap it in a RuntimeException (for example, new RuntimeException(new IOException()), but then you lose the ability to catch the checked exception in the caller method, because you have changed the type of the exception.

There is another trick you can use. I first noticed this being done in the JDK's java.lang.Class#newInstance0 method:

private T newInstance0()
    throws InstantiationException, IllegalAccessException
{
    ...
    // Run constructor
    try {
        return tmpConstructor.newInstance((Object[])null);
    } catch (InvocationTargetException e) {
        Unsafe.getUnsafe().throwException(e.getTargetException());
        // Not reached
        return null;
    }
    ...
}
As shown above, the method throws an InvocationTargetException but only declares the exceptions InstantiationException and IllegalAccessException to be thrown! The Sun utility class sun.misc.Unsafe is being used to throw a checked exception without declaring it. We could use the same approach, but unfortunately our code throws a security exception when we try to use it: java.lang.SecurityException: Unsafe. However, we can bypass this by using reflection to get hold of the unsafe variable directly, as shown below:
public void go() {
  getUnsafe().throwException(new IOException());
}

private Unsafe getUnsafe() {
  try {
    Field field = Unsafe.class.getDeclaredField("theUnsafe");
    field.setAccessible(true);
    return (Unsafe) field.get(null);
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}
So, why did the JDK developers do it this way? I don't know, but I'm sure they had a good reason. Although this is a neat trick to impress your friends, I wouldn't recommend using it. This is non-standard, undocumented, Sun-specific code which could change in future releases and may not be compatible with all JVMs. It also makes code harder to understand for others and violates the POLA.

Further Reading:
Avoiding Checked Exceptions [O'Reilly]