Search code examples
jakarta-eequartz-schedulercdi

Custom CDIJobFactory for using Quartz with CDI


I'm developing a Javaee7 webapp to run over Wildfly 10.0.0, using CDI for dependency injection, and want to schedule some jobs with Quartz. This is the class that schedules the job:

@Singleton
@Startup
public class ScheduledJobsManager {

    private Scheduler scheduler;

    @Inject
    private CdiJobFactory jobFactory;

    @PostConstruct
    public void postConstruct() {
        JobDetail jobDetail = null;
        CronTrigger trigger = null;
        scheduler = new StdSchedulerFactory().getScheduler();
        scheduler.setJobFactory(jobFactory);
        boolean scheduled = false;

        jobDetail = JobBuilder.newJob(ImagesProcessJob.class)
        .withIdentity("imageProcessJob", "mediaProxyJobs")
        .build();
        trigger = TriggerBuilder.newTrigger().withIdentity("imageProcessTrigger", "mediaProxyTriggers")
                .withSchedule(CronScheduleBuilder.cronSchedule(imageProcessScheduleStr)).build();
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }

    @PreDestroy
    public void preDestroy() {
        if (scheduler != null && scheduler.isStarted()) {
            scheduler.shutdown(false);
        }
    }
}

This is the custom JobFactory to allow using CDI on Quartz:

@Named
public class CdiJobFactory implements JobFactory {

  @Inject
  private BeanManager beanManager;

  @Override
  public Job newJob(TriggerFiredBundle bundle, Scheduler Scheduler) throws SchedulerException {
    JobDetail jobDetail = bundle.getJobDetail();
    Class<? extends Job> jobClazz = jobDetail.getJobClass();
    Bean<?> bean = beanManager.getBeans(jobClazz).iterator().next();
    CreationalContext<?> ctx = beanManager.createCreationalContext(bean);
    return (Job) beanManager.getReference(bean, jobClazz, ctx);
  }
}

And this is the Job:

@Named
public class ImagesProcessJob  implements Job {
    @Inject
    private MyManager myManager;

    public ImagesProcessJob() {}

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        try {
            myManager.doTheJob();
        } catch(Exception e) {
            throw new JobExecutionException(e);
        }
    }
}

When I try to deploy the war file, I get this error:

16:03:19,524 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-3) MSC000001: Failed to start service jboss.deployment.unit."imageproxy.war".WeldStartService: org.jboss.msc.service.StartException in service jboss.deployment.unit."imageproxy.war".WeldStartService: Failed to start service
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1904)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type CdiJobFactory with qualifiers @Default
  at injection point [BackedAnnotatedField] @Inject private cat.xavisan.imaging.imageproxy.schedule.ScheduledJobsManager.jobFactory
  at cat.xavisan.imaging.imageproxy.schedule.ScheduledJobsManager.jobFactory(ScheduledJobsManager.java:0)

    at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:359)
    at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:281)
    at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:134)
    at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:155)
    at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:518)
    at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:68)
    at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:66)
    at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:63)
    at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:56)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
    at org.jboss.threads.JBossThread.run(JBossThread.java:320)

This is the content of beans.xml

 <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       version="1.2" bean-discovery-mode="annotated">

</beans>

Any idea of what's going wrong?


Solution

  • The problem is that you're using bean-discovery-mode="annotated" but not providing bean defining annotations on your classes.

    Ideally, @Named is only used for UI type interaction, and few other vendor specific capabilities. Its a qualifier. To make this work, I would annotate it using @ApplicationScoped instead of @Named.

    Reference within the CDI Spec: http://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#bean_defining_annotations