Friday, September 9, 2011

Inject instances in Quartz jobs with Google Guice

This post explains how to use Quartz 2 with Google Guice. Before you read any further, I assume you have some basic understanding about Guice and Quartz 2, and what problems they solve.

Normally, Quartz will instantiate job classes itself. You only need to supply the job class, so Quartz knows which class to instantiate when a job is ready to be executed. The job itself is pretty hidden (encapsulated) by Quartz.

In some cases, you want to let Google Guice inject references of (singleton) objects to the job. Such as a Data Access Object, so the job can access or store data.

The solution is to supply our own job factory to the Quartz class that is responsible for job instantiation. Our factory will use Guice to create instances for Quartz to use.

package com.javaeenotes.guicequartz;

import org.quartz.Job;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;

import com.google.inject.Inject;
import com.google.inject.Injector;

public class MyJobFactory implements JobFactory {

    @Inject
    private Injector injector;


    @Override
    public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler)
            throws SchedulerException {

        return (Job) injector.getInstance(
            bundle.getJobDetail().getJobClass());
    }
}
To complete this example, we need an example DAO class, and a Quartz job class that will hold the injected DAO object.
package com.javaeenotes;

public interface Dao {
    public abstract String getData();
}
package com.javaeenotes;

import com.google.inject.Singleton;

@Singleton
public class DaoImpl implements Dao {

  @Override
  public String getData() {
    return "Data from DAO.";
  }
}
package com.javaeenotes;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import com.google.inject.Inject;

public class MyJob implements Job {

  @Inject
  private Dao dao;


  @Override
  public void execute(JobExecutionContext context)
        throws JobExecutionException {

    System.out.println(dao.getData());
  }
}
Now, we need a Guice module that defines and maps the factory and DAO classes.
package com.javaeenotes;

import org.quartz.spi.JobFactory;
import com.google.inject.AbstractModule;

public class MyModule extends AbstractModule {

  @Override
  protected void configure() {

    bind(JobFactory.class).to(MyJobFactory.class);
    bind(Dao.class).to(DaoImpl.class);
  }
}
Finally, a Main class to demonstrate the application:
package com.javaeenotes;

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class Main {

  public void run() {

    // The Guice injector used to create instances.
    Injector injector = Guice.createInjector(new MyModule());

    // Object that contains the job class.
    JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
            .withIdentity("jobId", "jobGroup").build();

    // Create the trigger that will instantiate and execute the job.
    // Execute the job with a 3 seconds interval.
    Trigger trigger = TriggerBuilder
            .newTrigger()
            .withIdentity("triggerId")
            .withSchedule(
                    SimpleScheduleBuilder.simpleSchedule()
                            .withIntervalInSeconds(3).repeatForever())
            .build();

    try {
      // Retrieve the Quartz scheduler to schedule the job.
      SchedulerFactory schedulerFactory = new StdSchedulerFactory();
      Scheduler scheduler = schedulerFactory.getScheduler();

      // Here we tell the Quartz scheduler to use our factory.
      scheduler.setJobFactory(injector.getInstance(MyJobFactory.class));
      scheduler.scheduleJob(jobDetail, trigger);

      // Start the scheduler.
      scheduler.start();
    } catch (SchedulerException e) {
      e.printStackTrace();
    }

    try {
      Thread.sleep(10000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }


  public static void main(String[] args) {
    new Main().run();
  }
}
This will output the following every 3 seconds:
Data from DAO.

Installation of Quartz 2 and Google Guice

If you use Maven 2 for your project, you can install both frameworks by simply adding the following configuration to your pom.xml file.

  org.quartz-scheduler
  quartz
  2.0.2



  com.google.inject
  guice
  3.0

5 comments:

  1. You seem to be missing the MyJobFactory class. It would be nice to see an example, at least a basic one.

    ReplyDelete
  2. Thanks for pointing this out. I will add the class as soon as possible.

    ReplyDelete
  3. I accidentally included the MyModule class definition twice. I replaced the first MyModule class with the MyJobfactory class, now.

    ReplyDelete
  4. thanks very much. It works fine

    ReplyDelete
  5. Article from 2011 and still very useful today! thanks for this article. very helpful

    ReplyDelete