How to use the Quartz Scheduler with the Demoiselle?

3

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

The job should call a method from a Business Controller (BC), which invokes a DAO to change the database. However, I could not inject the CB into the job; even passing the BC to the job otherwise, when calling the BC from the job, the application throws a ContextNotActiveException .

How do I call a method from a BC from a Quartz job?

    
asked by anonymous 29.01.2014 / 17:16

2 answers

1

In 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 these cases, there is an active RequestContext that allows you to use @Inject .

The problem is that the job runs on a separate thread, which does not have access to a context. To resolve 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 destroying 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();
  }
}
    
29.01.2014 / 17:16
1

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

In this case (EE application, I suppose) I suggest a very different approach than the one 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 "gateway" with Servlet or REST. In your client application (the task scheduler) you would consume an HTTP service from the application where the entire CDI context would be ready and functional.

See that this is the solution adopted by leading PaaS and IaaS (Cloud Computing) such as Google AppEngine and Redhat Openshift.

For SE applications, they are already solved on their own without the need for external services or schedulers. Your case has a lot more expensive EE applications and this was the focus of the answer.

    
11.12.2014 / 18:52