måndag 13 februari 2012

Integrate Quartz.NET with your favourite IoC container

I've used a job scheduler library called Quartz.NET in a couple of projects. This blogpost describes how to integrate it with your favourite flavoured IoC-container.

This applies to Quartz.NET version 1.0.3 that is also available on NuGet by package name "Quartz".

Jobs are implemented by implementing one of the job-interfaces IJob, IInterruptableJob or IStatefulJob (they are all based on IJob). The job is then registered with a scheduler and a trigger.

The initialization code below is also described in Lesson 1 of Quartz.NET tutorial:
// construct a scheduler factory
ISchedulerFactory schedFact = new StdSchedulerFactory();

// get a scheduler
IScheduler sched = schedFact.GetScheduler();
sched.Start();

// construct job info
JobDetail jobDetail = new JobDetail("myJob", null, typeof(HelloJob));
// fire every hour
Trigger trigger = TriggerUtils.MakeSecondlyTrigger(5);
trigger.Name = "myTrigger";
sched.ScheduleJob(jobDetail, trigger);
This is fine as long as the job doesn't require a dependency. But what if your Job implementation demands a dependency?
public class HelloJob : IJob
{
    private readonly ILogger _logger;

    public HelloJob(ILogger logger)
    {
        _logger = logger;
    }

    public void Execute(JobExecutionContext context)
    {
        _logger.Log(@"Oh Hai \o/");
    }
}
Then you need to roll your own IJobFactory implementation. My implementation with my favourite IoC container Castle Windsor is described below:
public class WindsorJobFactory : IJobFactory
{
    private readonly IWindsorContainer _container;

    public WindsorJobFactory(IWindsorContainer container)
    {
        _container = container;
    }

    public IJob NewJob(TriggerFiredBundle bundle)
    {
        return (IJob)_container.Resolve(bundle.JobDetail.JobType);
    }
}
The JobFactory instance then needs to be assigned to the scheduler in the initialization code:
var container = new WindsorContainer();
IJobFactory jobFactory = new WindsorJobFactory(container);

ISchedulerFactory schedFact = new StdSchedulerFactory();

IScheduler sched = schedFact.GetScheduler();
sched.JobFactory = jobFactory;
sched.Start();
I'm aware that you should not call your IoC-container directly, instead adhere to The Hollywood principle, but I think its ok in this case because it's the initialization of the infrastructure. The job implementations must also be registered with your IoC-container:
public class JobRegistrar
{
    private readonly IWindsorContainer _container;

    public JobRegistrar(IWindsorContainer container)
    {
        _container = container;
    }

    private static IEnumerable GetJobTypes()
    {
        return AppDomain.CurrentDomain.GetAssemblies().ToList()
            .SelectMany(s => s.GetTypes())
            .Where(p => typeof(IJob).IsAssignableFrom(p) && !p.IsInterface);
    }

    public void RegisterJobs()
    {
        var jobTypes = GetJobTypes();
        foreach (Type jobType in jobTypes)
        {
           _container.Register(Component.For(jobType).ImplementedBy(jobType).LifeStyle.Transient);
        }
    }
}
That's all for tonight folks! All code can be found on my GitHub account

2 kommentarer:

  1. There's been a contrib project to integrate Quartz.NET and Windsor for 3 years now, please see http://bugsquash.blogspot.com/2009/03/windsor-facility-for-quartznet.html and https://github.com/castleprojectcontrib/QuartzNetIntegration . It supports a lot more than just managing jobs. Let's avoid the fragmentation of work efforts and work on this contrib project.

    SvaraRadera
    Svar
    1. Thank you for your comment!

      The contrib project looks nice, I will use it in the future for Windsor integration.

      This blog post shows how to integrate Quartz.NET with any IoC container. The example is using Castle Windsor, it could have been any IoC.

      Radera