How to use Quartz Scheduler with Demoiselle?

Asked

Viewed 659 times

3

I created a job using Quartz Scheduler within a Java web application that uses the Demoiselle framework with JSF and Tomcat 7.

The job must call a Business Controller (BC) method, which calls a DAO to change the database. However, I was unable to inject the BC into the job; even passing the BC to the job otherwise, by calling the BC from the job, the application launches a ContextNotActiveException.

How to call a BC method from a Quartz job?

2 answers

1

This is a general CDI "problem". For Inject, Interceptor, Scopes, etc to work a running CDI environment is required. These environments are available in Java EE 6+ containers or SE implementations such as Weld SE, for example.

In this case (EE application, I suppose) I suggest a very different approach to what you are trying. Use your task scheduler (Quartz, cron, IFTTT, or whatever) to trigger HTTP calls to your application.

In the application you can implement the "access port" with Servlet or REST. In your client application (the task scheduler) would consume an application HTTP service where the entire CDI context would be ready and functional.

Note that this is the solution adopted by the main Paas and Iaas (Cloud Computing) like Google Appengine and Redhat Openshift.

For SE applications, they already resolve themselves without the need for external services or schedulers. Your case has much more expensive EE applications and this was the focus of the answer.

  • Yeah, my case is EE. I understand that there are trade-offs: you need to have the application URL, and the URL may vary according to the deployment environment (testing, production, etc.); a malicious visitor who discovers the "access port" URL can perform the task more often than desired. Because of that I will keep my answer, but if I need to do something similar in the future I will consider your solution.

1


In the most common cases, the BC code is called from a Managed bean (MB) whose execution was originated by an HTTP request made by the web browser. In such cases, there is an active Requestcontext that allows you to use the @Inject.

The problem is that the job runs on a separate thread, which has no access to a context. To solve this, you must pass the BC to the job and, within the job, create a request context.

The class that creates the job must inject the BC and a Requestcontext; when creating the job, these objects must be passed to the job through a Jobdatamap:

@BusinessController
public class Agendador {
  @Inject
  private MinhaClasseBC meuObjetoBC;
  @Inject
  private BoundRequestContext requestContext;

  public void agendaJob() throws SchedulerException {
    JobDataMap jobData = new JobDataMap();
    jobData.put("requestContext", requestContext);
    jobData.put("bc", meuObjetoBC);

    JobDetail job = newJob(ClasseDoJob.class)
        .withIdentity("id", "grupo")
        .setJobData(jobData)
        .build();

    // aqui vem o código da trigger e do agendamento
  }
}

The job class then takes these objects and creates the context before calling the BC (and destroys after the call):

public class ClasseDoJob implements Job {
  @Override
  public void execute(JobExecutionContext jobContext) throws JobExecutionException {
    JobDataMap jobData = jobContext.getJobDetail().getJobDataMap();
    BoundRequestContext ctx = (BoundRequestContext)jobData.get("requestContext");
    MinhaClasseBC bc = (MinhaClasseBC)jobData.get("bc");

    ctx.associate(new HashMap<String, Object>());
    ctx.activate();

    bc.metodoQueFazAlgumaCoisa();

    ctx.invalidate();
    ctx.deactivate();
  }
}
  • Interestingly, this code only builds on Oracle’s JDK. On Openjdk, it cannot find the package org.jboss.weld.context.bound, where the class is BoundRequestContext.

  • Rectifying: the code compiles in both Openjdk and Oracle JDK. The problem I faced was a mix-up with the dependencies.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.