Quartz.NET meets Design Patterns

This is the third in a series of posts.

In the last post I showed you how I set up some tests for my implementation of retries with Quartz.NET. I repeatedly hinted at some neat tricks to make things more convenient so here they are.

Quartz.NET requires that your jobs only throw JobExecutionExceptions (as explained at the very bottom of this page). There are reasons why this restriction makes sense but I don’t want to litter my business logic with repetitions of the exact same exception handling code. I think that’s what DRY is all about. I don’t want to force all of my jobs to inherit from a specific base class either. At least not for the single purpose of following Quartz.NET’s rules for exception handling.

But by applying a decorator to my job classes I can fix that once and for all.

public class EnsureJobExecutionExceptionDecorator : IJob
  private readonly IJob inner;
  public EnsureJobExecutionExceptionDecorator(IJob inner)
    this.inner = inner;
  public void Execute(IJobExecutionContext context)
    catch (JobExecutionException)
    catch (Exception cause)
      throw new JobExecutionException(cause);

JobExecutionExceptions are simply rethrown. Which allows you to throw them in your job if you have to tweak what to tell the scheduler. All other exceptions become InnerExceptions of a new JobExecutionException. Done. Now that was easy.

But how do I ensure that each time Quartz.NET instantiates a job the decorator is in place?

By replacing the scheduler’s default IJobFactory with something more advanced. For my playground I derived from the PropertySettingJobFactory base class and use Unity to create my jobs.

private sealed class UnityJobFactory : PropertySettingJobFactory
  private readonly IUnityContainer container;
  public UnityJobFactory(IUnityContainer container)
    this.ThrowIfPropertyNotFound = false;
    this.WarnIfPropertyNotFound = true;
    this.container = container;
  public override IJob NewJob(
    TriggerFiredBundle bundle,
    IScheduler scheduler)
    Type jobType = bundle.JobDetail.JobType;
    IJob job = (IJob)this.container.Resolve(jobType);
    JobDataMap data = new JobDataMap();
    this.SetObjectProperties((object)job, data);
    return job;
  public override void ReturnJob(IJob job)

And then its a simple matter to configure Unity to wrap every job it creates with the EnsureJobExecutionExceptionDecorator. Not that hard is it?

And finally there is the code snippet that unfreezes my test thread when I’m done.

public class UnfreezeWhenJobShouldNotRunAgain : IRetryStrategy
  private readonly IRetryStrategy inner;
  private readonly ManualResetEvent reset;
  public UnfreezeWhenJobShouldNotRunAgain(
    IRetryStrategy inner,
    ManualResetEvent reset)
    this.inner = inner;
    this.reset = reset;
  public bool ShouldRetry(IJobExecutionContext context)
    bool shouldRetry = this.inner.ShouldRetry(context);
    if (!shouldRetry)
    return shouldRetry;
  public ITrigger GetTrigger(IJobExecutionContext context)
    return this.inner.GetTrigger(context);

Yet another decorator. Whenever the RetryJobListener from the first post of this series queries the IRetryStrategy wether a job should be run again the decorator checks for “yes” or “no”. And in case of a “no” it will set the ManualResetEvent and allow the test thread to continue.

So we have decorators, an abstract factory and dependency injection here. And all of that in less than 200 lines of code. All pieces short and to the point but by combining them you can build mighty powerful solutions that are still clean and easy to understand.

I hope you enjoyed the series and come back for another read. See you soon!


3 Responses to Quartz.NET meets Design Patterns

  1. Pingback: Quartz and other gems | Outlawtrail - .NET Development

  2. Anton Shcherbyna says:

    Do U can publish url to github and add it in your post?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: