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
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).
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);
}
}
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
}
}
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.
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.).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With