En ocasiones se hace necesario crear un EJB 3.1 que emplee la interfaz Schedule y normalmente es una tarea muy simple de realizar en Java. Sin embargo, cuando ese aplicativo debe correr en un ambiente de cluster sucede que la tarea se dispara en todos los miembros del cluster y esta situación no siempre es deseable.

En este artículo les explicamos como configurar WebLogic 12c (12.2.1.3.0) para que el EJB se ejecute en únicamente uno de los nodos, manteniendo los beneficios de hacer el deployment en el cluster. Es decir, su alta disponibilidad. Si el nodo donde corre la tarea falla; entonces de manera transparente será ejecutado por otro nodo en el cluster.

EJB con Timer


Nuestro ejemplo en Java es bastante sencillo y no tiene nada diferente a lo dictado en la especificación. Observen que empleamos la anotación Schedule y establecemos que el proceso debe ejecutarse cada 5 minutos.

@Stateless
public class DemoTimerService implements java.io.Serializable {
    private static final Logger LOG = Logger.getLogger(DemoTimerService.class.getName());

    @Schedule(month = "*", hour = "*", dayOfMonth = "*", year = "*", minute = "*/5", second = "*", persistent = true)
    public void myTimer() {
        SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
        LOG.log(Level.WARNING, "Timer event: {0}", df.format(new Date()));
    }
}

Configuración del WebLogic


Paso 1. Estructuras

El primer paso que tenemos que hacer es configurar el cluster. WebLogic emplea dos tablas para manejar los timers ACTIVE y WEBLOGIC_TIMER; estos nombres se pueden cambiar de acuerdo a la normativa de nomenclatura que tengamos establecida. En nuestro caso, lo vamos a dejar de esa manera.

Estas dos tablas deben ser creadas en la base de datos que usaremos para contener esos detalles. Los archivos que tienen el ddl se encuentran en: $ORACLE_HOME/wlserver/server/db/oracle/920, en los archivos scheduler.ddl y leasing.ddl Esos scripts son específicos para Oracle, pues corresponden a la base de datos que usaremos de ejemplo; pero además provee los scripts para:

  • Sybase
  • MySQL
  • MSSQL
  • Informix
  • DB2

scheduler.ddl

CREATE TABLE WEBLOGIC_TIMERS (
  TIMER_ID VARCHAR2(100) NOT NULL,
  LISTENER BLOB NOT NULL,
  START_TIME NUMBER NOT NULL,
  INTERVAL NUMBER NOT NULL,
  TIMER_MANAGER_NAME VARCHAR2(500) NOT NULL,
  DOMAIN_NAME VARCHAR2(100) NOT NULL,
  CLUSTER_NAME VARCHAR2(100) NOT NULL,
  USER_KEY VARCHAR2(1000) UNIQUE,
  PRIMARY KEY (TIMER_ID, CLUSTER_NAME, DOMAIN_NAME)
);

leasing.ddl

CREATE TABLE ACTIVE (
  SERVER VARCHAR2(255) NOT NULL,
  INSTANCE VARCHAR2(255) NOT NULL,
  DOMAINNAME VARCHAR2(255) NOT NULL,
  CLUSTERNAME VARCHAR2(255) NOT NULL,
  TIMEOUT DATE,
  PRIMARY KEY (SERVER, DOMAINNAME, CLUSTERNAME)
);

Paso 2. Scheduling

Una vez que creamos estas estructuras, procedemos a ir a la consola de administración de WebLogic. Entrando a las propiedades del cluster y seleccionando la opción Scheduling. Como se aprecia en la siguiente imagen.

Propiedades

Ahí debemos seleccionar el recurso JDBC que tiene la configuración al servidor en donde se crearon las dos estructuras señaladas.

Paso 3. Migration

En la consola, de nuevo en cluster, seleccionamos la opción Migration y seleccionamos los machines que pueden emplear para migrar a un servidor que ha fallado. De particular importancia es que se tenga seleccionado Database y que el recurso JDBC seleccionado sea el mismo que se creo inicialmente. Noten que el nombre por omisión de la tabla es ACTIVE.

Migration

Paso 4. Persistent Stores

En la consola, seleccionamos la opción Persistent Stores y creamos un nuevo JDBC Store, como mostramos seguidamente.

Persistent

Ahi debemos seleccionar el datasource (que el mismo que hemos venido usando) y disponer de un nombre lógico. En nuestro caso escribimos storeEJB

Persistent

Internamente, eso va a crear una tabla WLSTORE para cada servidor del cluster.

Paso 5. Descriptor

De vuelta en nuestro aplicativo, debemos agregar un archivo descriptor específico para WebLogic. Este se llama weblogic-ejb-jar.xml y se ve de la siguiente manera:

<?xml version = '1.0' encoding = 'windows-1252'?>
<weblogic-ejb-jar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-ejb-jar http://xmlns.oracle.com/weblogic/weblogic-ejb-jar/1.7/weblogic-ejb-jar.xsd"
                  xmlns="http://xmlns.oracle.com/weblogic/weblogic-ejb-jar">
  <weblogic-enterprise-bean>
    <ejb-name>DemoTimerService</ejb-name>
    <stateless-session-descriptor>
      <timer-descriptor>
        <persistent-store-logical-name>storeEJB</persistent-store-logical-name>
      </timer-descriptor>
    </stateless-session-descriptor>
  </weblogic-enterprise-bean>
  <timer-implementation>Clustered</timer-implementation>
</weblogic-ejb-jar>

De ahí es importante tomar nota de:

  • ejb-name: Es el nombre del EJB
  • persistent-store-logical-name: Debe ser el nombre exacto que definimos en el paso 4.

Y eso es todo; una vez que hacemos deploy de nuestro aplicativo en el servidor veremos que se ejecuta en sólo un nodo del cluster.

El código fuente de este artículo esta disponible en https://github.com/FlechaRoja/EJBTimer

Esperamos que este artículo les sea de utilidad.