Preloader image
Este exemplo é um pouco datado. Ele antecede a API de agendamento que foi adicionado ao EJB 3.1. Aplicações modernas devem usar a API de agendamento, que possui muitos, se não todos, os mesmos recursos que o Quartz. De fato, o Quartz é o mecanismo que aciona o suporte @Schedule e ScheduleExpression no OpenEJB e TomEE.

Apesar de datado de uma perspectiva de programação, ainda é uma excelente referência sobre como conectar e testar um Java EE Resource Adapter personalizado..

Estrutura do projeto

Como os arquivos .rar não se saem bem em uma estrutura padrão do classpath, o objetivo é efetivamente desembrulhar o .rar para que suas dependências estejam no classpath e seu arquivo ra.xml ser encontrado e verificado pelo OpenEJB.

Fazemos isso criando um módulo mini-maven para representar o .rar no termos maven. O pom.xml do módulo rar declara todos os jars que estariam dentro do .rar como dependências automatizadas. O arquivo ra.xml é adicionado ao projeto em src/main/resources/META-INF/ra.xml onde será visível para outros módulos.

quartz-app
quartz-app/pom.xml
quartz-app/quartz-beans
quartz-app/quartz-beans/pom.xml
quartz-app/quartz-beans/src/main/java/org/superbiz/quartz/JobBean.java
quartz-app/quartz-beans/src/main/java/org/superbiz/quartz/JobScheduler.java
quartz-app/quartz-beans/src/main/java/org/superbiz/quartz/QuartzMdb.java
quartz-app/quartz-beans/src/main/resources/META-INF
quartz-app/quartz-beans/src/main/resources/META-INF/ejb-jar.xml
quartz-app/quartz-beans/src/test/java/org/superbiz/quartz/QuartzMdbTest.java
quartz-app/quartz-ra
quartz-app/quartz-ra/pom.xml
quartz-app/quartz-ra/src/main/resources/META-INF
quartz-app/quartz-ra/src/main/resources/META-INF/ra.xml

ra.xml

O conector em questão possui adaptadores de recursos de entrada e saída. O adaptador de recursos de entrada pode ser usado para conduzir message driven beans (MDBs). O adaptador de recursos de saída, QuartzResourceAdapter, pode ser injetado em qualquer componente via`@Resource` e usado para originar e enviar mensagens ou eventos.

<connector xmlns="http://java.sun.com/xml/ns/j2ee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
           http://java.sun.com/xml/ns/j2ee/connector_1_5.xsd"
           version="1.5">

  <description>Quartz ResourceAdapter</description>
  <display-name>Quartz ResourceAdapter</display-name>

  <vendor-name>OpenEJB</vendor-name>
  <eis-type>Quartz Adapter</eis-type>
  <resourceadapter-version>1.0</resourceadapter-version>

  <resourceadapter id="QuartzResourceAdapter">
    <resourceadapter-class>org.apache.openejb.resource.quartz.QuartzResourceAdapter</resourceadapter-class>

    <inbound-resourceadapter>
      <messageadapter>
        <messagelistener>
          <messagelistener-type>org.quartz.Job</messagelistener-type>
          <activationspec>
            <activationspec-class>org.apache.openejb.resource.quartz.JobSpec</activationspec-class>
          </activationspec>
        </messagelistener>
      </messageadapter>
    </inbound-resourceadapter>

  </resourceadapter>
</connector>

Usando o Adaptador de Recursos de Saída

Aqui vemos o adaptador de recursos de saída usado em um bean de sessão stateless para agendar uma tarefa que será executada pelo MDB

package org.superbiz.quartz;

import org.apache.openejb.resource.quartz.QuartzResourceAdapter;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SimpleTrigger;

import jakarta.ejb.Stateless;
import javax.naming.InitialContext;
import java.util.Date;

@Stateless
public class JobBean implements JobScheduler {

    @Override
    public Date createJob() throws Exception {

        final QuartzResourceAdapter ra = (QuartzResourceAdapter) new InitialContext().lookup("java:openejb/Resource/QuartzResourceAdapter");
        final Scheduler s = ra.getScheduler();

        //Add a job type
        final JobDetail jd = new JobDetail("job1", "group1", JobBean.MyTestJob.class);
        jd.getJobDataMap().put("MyJobKey", "MyJobValue");

        //Schedule my 'test' job to run now
        final SimpleTrigger trigger = new SimpleTrigger("trigger1", "group1", new Date());
        return s.scheduleJob(jd, trigger);
    }

    public static class MyTestJob implements Job {

        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("This is a simple test job to get: " + context.getJobDetail().getJobDataMap().get("MyJobKey"));
        }
    }
}

Recebendo dados do adaptador de recursos de entrada

package org.superbiz.quartz;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import jakarta.ejb.ActivationConfigProperty;
import jakarta.ejb.MessageDriven;

@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "cronExpression", propertyValue = "* * * * * ?")})
public class QuartzMdb implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("Executing Job");
    }
}

Caso de teste

package org.superbiz.quartz;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Date;
import java.util.Properties;

public class QuartzMdbTest {

    private static InitialContext initialContext = null;

    @BeforeClass
    public static void beforeClass() throws Exception {

        if (null == initialContext) {
            Properties properties = new Properties();
            properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.core.LocalInitialContextFactory");

            initialContext = new InitialContext(properties);
        }
    }

    @AfterClass
    public static void afterClass() throws Exception {
        if (null != initialContext) {
            initialContext.close();
            initialContext = null;
        }
    }

    @Test
    public void testLookup() throws Exception {

        final JobScheduler jbi = (JobScheduler) initialContext.lookup("JobBeanLocal");
        final Date d = jbi.createJob();
        Thread.sleep(500);
        System.out.println("Scheduled test job should have run at: " + d.toString());
    }

    @Test
    public void testMdb() throws Exception {
        // Sleep 3 seconds and give quartz a chance to execute our MDB
        Thread.sleep(3000);
    }
}

Executando

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.superbiz.quartz.QuartzMdbTest
Apache OpenEJB 4.0.0-beta-1    build: 20111002-04:06
http://tomee.apache.org/
INFO - openejb.home = /Users/dblevins/examples/quartz-app/quartz-beans
INFO - openejb.base = /Users/dblevins/examples/quartz-app/quartz-beans
INFO - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service)
INFO - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction Manager)
INFO - Found ConnectorModule in classpath: /Users/dblevins/examples/quartz-app/quartz-ra/target/quartz-ra-1.0.jar
INFO - Found EjbModule in classpath: /Users/dblevins/examples/quartz-app/quartz-beans/target/classes
INFO - Beginning load: /Users/dblevins/examples/quartz-app/quartz-ra/target/quartz-ra-1.0.jar
INFO - Extracting jar: /Users/dblevins/examples/quartz-app/quartz-ra/target/quartz-ra-1.0.jar
INFO - Extracted path: /Users/dblevins/examples/quartz-app/quartz-ra/target/quartz-ra-1.0
INFO - Beginning load: /Users/dblevins/examples/quartz-app/quartz-beans/target/classes
INFO - Configuring enterprise application: /Users/dblevins/examples/quartz-app/quartz-beans/classpath.ear
INFO - Configuring Service(id=Default Stateless Container, type=Container, provider-id=Default Stateless Container)
INFO - Auto-creating a container for bean JobBean: Container(type=STATELESS, id=Default Stateless Container)
INFO - Configuring Service(id=QuartzResourceAdapter, type=Resource, provider-id=QuartzResourceAdapter)
INFO - Configuring Service(id=quartz-ra-1.0, type=Container, provider-id=Default MDB Container)
INFO - Enterprise application "/Users/dblevins/examples/quartz-app/quartz-beans/classpath.ear" loaded.
INFO - Assembling app: /Users/dblevins/examples/quartz-app/quartz-beans/classpath.ear
INFO - Jndi(name=JobBeanLocal) --> Ejb(deployment-id=JobBean)
INFO - Jndi(name=global/classpath.ear/quartz-beans/JobBean!org.superbiz.quartz.JobScheduler) --> Ejb(deployment-id=JobBean)
INFO - Jndi(name=global/classpath.ear/quartz-beans/JobBean) --> Ejb(deployment-id=JobBean)
INFO - Created Ejb(deployment-id=JobBean, ejb-name=JobBean, container=Default Stateless Container)
INFO - Created Ejb(deployment-id=QuartzMdb, ejb-name=QuartzMdb, container=quartz-ra-1.0)
Executing Job
INFO - Started Ejb(deployment-id=JobBean, ejb-name=JobBean, container=Default Stateless Container)
INFO - Started Ejb(deployment-id=QuartzMdb, ejb-name=QuartzMdb, container=quartz-ra-1.0)
INFO - Deployed Application(path=/Users/dblevins/examples/quartz-app/quartz-beans/classpath.ear)
This is a simple test job to get: MyJobValue
Scheduled test job should have run at: Fri Oct 28 17:05:12 PDT 2011
Executing Job
Executing Job
Executing Job
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.971 sec

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0