Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

can I use CDI injection into quartz-scheduler jobs?

I'm using Glassfish and CDI for injection, (mostly) successfully. I can't seem to get Quartz jobs to work with injection- beans annotated with @Inject never get injected.

Is Quartz using some kind of different classloader that's preventing injection from happening?

I'm configuring Quartz like so in my web.xml:

<context-param>
    <param-name>quartz:config-file</param-name>
    <param-value>quartz.properties</param-value>
</context-param>
<context-param>
    <param-name>quartz:shutdown-on-unload</param-name>
    <param-value>true</param-value>
</context-param>
<context-param>
    <param-name>quartz:wait-on-shutdown</param-name>
    <param-value>false</param-value>
</context-param>
<context-param>
    <param-name>quartz:start-scheduler-on-load</param-name>
    <param-value>true</param-value>
</context-param>

<listener>
    <listener-class>
        org.quartz.ee.servlet.QuartzInitializerListener
    </listener-class>
</listener>

My quartz.properties looks like:

org.quartz.scheduler.instanceName = MyScheduler
org.quartz.scheduler.instanceId = 1
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false

org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 3

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
org.quartz.plugin.jobInitializer.fileNames = quartz-config.xml
org.quartz.plugin.jobInitializer.scanInterval = 10
org.quartz.plugin.jobInitializer.wrapInUserTransaction = false
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
like image 248
George Armhold Avatar asked Jan 27 '11 04:01

George Armhold


1 Answers

I looked into the github library mentioned by @george-armhold, but found it not mature.

Nevertheless, I found another solution.

Take a look into this blog post: DevSoap: Injecting CDI Managed Beans into Quartz Jobs. It describes a class CdiJobFactory.java, which will do the job (written in either Groovy or Scala, but not kotlin or java).

Implementation in java

CDI-capable Job Factory

Same CdiJobFactory in java:

/**
 * CDI Job factory. Quartz will produce CDI managed beans.
 */
@ApplicationScoped
public class CdiJobFactory implements JobFactory {

  @Inject
  BeanManager beanManager;

  @Override
  public Job newJob(final TriggerFiredBundle bundle, final Scheduler scheduler) throws SchedulerException {
    final Class<Job> jobClazz = (Class<Job>) bundle.getJobDetail().getJobClass();
    final Bean<Job> bean = (Bean<Job>) beanManager.getBeans(jobClazz).stream().findAny().orElseThrow(IllegalStateException::new);
    final CreationalContext<Job> ctx = beanManager.createCreationalContext(bean);

    return (Job) beanManager.getReference(bean, jobClazz, ctx);
  }

}

Injection of CDI Job Factory in your listener classes

Now in your Listener class which will load on startup, do this:

@ApplicationScoped
public class Listener implements ServletContextListener {

  @Inject
  public Listener(final CdiJobFactory jobFactory) {
    this.jobFactory = jobFactory;
  }

  @Override
  public void contextInitialized(final ServletContextEvent servletEvent) {
    LOG.info("Initializing Listener");

    try {
      scheduler = StdSchedulerFactory.getDefaultScheduler();
      scheduler.setJobFactory(jobFactory);
    } catch (final SchedulerException | RuntimeException schedEx) {
      LOG.error("Problem loading Quartz!", schedEx);
    }

   // register your jobs here
  }
}

Creating jobs

Look again at the blog post. Just annotate them with @Dependent or @ApplicationScoped (depending on your use case) and you are fine.

Don't forget to create two constructors: A public no-arg constructor and a public constructor annotated with @Inject and the needed beans as parameters. I omnited the first constructor for brevity.

If you are going to test with needle4j, injections will only get picked up with a field annotated @Inject. But you can have both, weld won't complain.

Other alternatives

You can also take a look at Apache Deltaspike. It will also handle other CDI implementations. This is useful if you run your application on various application servers with different implementations (like JBoss, Websphere, Liberty Profile, TomEE, Glassfish, etc.).

like image 185
Benjamin Marwell Avatar answered Sep 24 '22 14:09

Benjamin Marwell