Ivan Stoyanov
Ivan Stoyanov

Reputation: 5482

How to inject dependency in a Quartz job using Dagger 2

I am using Dagger 2 as my DI framework and I am providing a singleton class instance with it.

I also use Quartz Scheduler to schedule jobs. Is there any way to inject the singleton class into the Quartz job?

Dagger 2 module:

@Module
public class MyModule {

    @Provides
    @Singleton
            Messager provideMessager() {

        return new CustomMessager();
    }
}

Dagger 2 component:

@Component(modules = MyModule.class)
@Singleton
public interface MyComponent {

    Messager messager();
}

Quartz Job:

public class MyJob implements Job {

    // @Inject
    Messager messager;

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

        messager.sendMessage("Hello.");
    }

}

EDIT

I have created a MyJobScheduler class that calls the Quartz Job:

    public class MyJobScheduler {


        public void scheduleJob() {

            JobDetail myJob = JobBuilder.newJob(MyJob.class)
                                            .withIdentity("myJobId", "Group1")
                                            .build();

            Trigger trigger = TriggerBuilder.newTrigger()
                                            .withIdentity("myTriggerId", "Group1")
                                            .startNow()
                                            .build();

                Scheduler scheduler = new org.quartz.impl.StdSchedulerFactory().getScheduler();

                scheduler.start();


    scheduler.scheduleJob(myJob, trigger);

    }
}

EDIT 2

So I managed to configure it to work, but I don't know if this is the correct approach.

First I created a DependencyResolver class, which I use as a singleton:

public class DependencyResolver {

    private static DependencyResolver _instance = null;

    private static MyComponent _myComponent;

    public static MyComponent getMyComponent() {
        return _myComponent;
    }

    protected DependencyResolver() {
        // Exists only to defeat instantiation.
    }

    public static void initialize() {

        _myComponent = DaggerMyComponent.builder().build();
    }
}

Then I called the initialize method in the main method:

DependencyResolver.initialize();

MyComponent myComponent = DependencyResolver.getMyComponent();

And I used the DependencyResolver in MyJob class to get the Messager singleton instance.

public class MyJob implements Job {

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

    MyComponent myComponent = DependencyResolver.getMyComponent();

    Messager messager = myComponent.messager();
    messager.sendMessage("Hello.");
    }
}

Is this the correct way to solve this issue? Any input will be greatly appreciated.

Upvotes: 4

Views: 4381

Answers (1)

G. Lombard
G. Lombard

Reputation: 3679

Your EDIT 2 DependencyResolver approach kind of defeats the whole reason to use Dagger to inject the dependencies, because your job gets the dependency from a singleton provider. :-) It completely bypasses the benefit of Dagger, so you might as well just have a singleton on the source dependency itself, like: Messager messager = CustomMessager.getInstance() or something like that.

One reason to use Dependency Injection, is to assist with unit testing, and in this case you're losing the ability to mock your Messager implementation in a unit test.

The proper way to use dependency injection with Quartz jobs is mentioned in the API doc for JobFactory: "This interface may be of use to those wishing to have their application produce Job instances via some special mechanism, such as to give the opertunity for dependency injection."

The trick is to create your own job factory that extends SimpleJobFactory and then you have an opportunity to initialize/inject the job's dependencies, something like this:

public class MyJobFactory extends SimpleJobFactory {
    private final MyComponent component;

    @Inject
    public MyJobFactory(MyComponent component) {
        this.component = component;
    }

    @Override
    public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
        final Job job = super.newJob(bundle, scheduler);
        if (job instanceof MyJob) {
            component.inject((MyJob) job);
        }
        return job;
    }
}

And then you tell the scheduler to use your job factory:

scheduler.setJobFactory(myJobFactory);

See the full code here on GitHub

Upvotes: 6

Related Questions