Glassfish Cluster and EJB Timer

Glassfish Cluster and EJB Timer
G.Morreale

Introduzione:

Una delle caratteristiche delle specifiche ejb, è il servizio "Timer Service".
Esso consente di attivare timer per determinati periodi di tempo o allo scadere di determinate date.


Tale servizio si appoggia su un database embedded in glassfish.
Navigando nell'admin console tra i connection pool (vedi risorse JDBC) è possibile notare infatti il connection pool "__TimerPool" utilizzato dalla risorsa __TimerPool.

Tale risorsa è disponibile di default solo per il target "server", quindi il cluster non è nemmeno a conoscenza di un db in grado di supportare il timer service.

Inoltre il database embedded non risulta adeguato in quanto le istanze del cluster non possono accedere a tale db e conoscere quindi lo stato della gestione del timer service.

Dovendo distribuire su glassfish cluster un EJB che utilizza il timer service bisogna configurare un db esterno in grado di supportare le transazioni e garantire che solo un istanza gestisca il timeout senza replicare gli eventi più del necessario.

Passi per la Configurazione:

Per configurare il cluster affinchè esso supporti il servizio EJB Timer è necessario seguire i seguenti passi

- Creare un db (es. TIMER_DB)

- Creare all'interno una tabella usando gli script sql che si trovano all'interno di $GLASSFISH_HOME/lib/install/databases
   Lo script mysql non è presente lo potete copiare da qui:

CREATE TABLE EJB__TIMER__TBL (
    CREATIONTIMERAW      BIGINT        NOT NULL,
    `BLOB`               BLOB,
    TIMERID              VARCHAR(255)  NOT NULL,
    CONTAINERID          BIGINT        NOT NULL,
    OWNERID              VARCHAR(255)  NOT NULL,
    STATE                INTEGER       NOT NULL,
    PKHASHCODE           INTEGER       NOT NULL,
    INTERVALDURATION     BIGINT        NOT NULL,
    INITIALEXPIRATIONRAW BIGINT        NOT NULL,
    LASTEXPIRATIONRAW    BIGINT        NOT NULL,
    CONSTRAINT PK_EJB__TIMER__TBL PRIMARY KEY (TIMERID) 
) ;

nota: il nome della tabella deve essere maiuscolo

- Creare all'interno di "Resource->Connection Pools" un pool in grado di comunicare con il db creato.

- Creare una risorsa jdbc che punta al connection pool appena creato.
La risorsa creata deve avere come target il cluster, per garantire ciò è possibile creare la risorsa cliccando su 
"Cluster->nome cluster->Resources->New->JDBC Resources"

- Andare sulla configurazione del cluster (Quella di default si chiama cluster-config), 
selezionare EJB-container, 
cliccare su EJB Timer Service 
settare nel campo Timer Datasource il nome della risorsa jdbc creata nel passo precedente.

- Riavviare il cluster.

Driver XA  e Possibili problematiche

Per fare un buon setup bisogna creare, come peraltro ho suggerito precedentemente, una risorsa jdbc apposita per il timer.
Tale scelta però può creare dei problemi nel momento in cui l'applicazione che fà uso del timer service contemporaneamente accede ad una risorsa jdbc differente (ad esempio mediante hibernate si effettua l'accesso al connection pool del db dell'applicazione).

In tal caso uno stesso bean deve accedere a due risorse contemporaneamente e garantire le proprietà ACID.
Per far ciò è necessario disporre di un driver JDBC XA-capable

Tale driver ovviamente deve essere disponibile nel classpath, ad esempio copiandolo nella directory /lib del dominio (di default domain1).
(nota: quando aggiorni una libreria è necessario riavviare l'application server)

Nel caso di mysql, il connector J (attualmente alla versione 5.1.6) è XA compatibile.

Oltre ad avere un driver con le suddette caratteristiche è necessario impostare il connection pool affinchè tali caratteristche vengano usate.
Quindi nel connection pool creato si dovrà settare come Resource Type "java.sql.XADataSource", nel campo DataSourceClassName invece bisognerà settare il corretto valore in base al dbms utilizzato

Nel caso di mysql tale classe è 
com.mysql.jdbc.jdbc2.optional.MysqlXADataSource


Purtroppo al momento il connector J crea dei problemi dovuti al bug:http://bugs.mysql.com/bug.php?id=35489

Di conseguenza durante il riavvio del timer (Ad esempio quando viene riavviata un instanza del cluster) si otterà la seguente eccezione:

INFO: Exception thrown from bean; nested exception is: java.lang.IllegalArgumentException: null source jav... 

Caused by: java.lang.IllegalArgumentException: null source
at java.util.EventObject.<init>(EventObject.java:38)
at javax.sql.StatementEvent.<init>(StatementEvent.java:39)
at
com.mysql.jdbc.jdbc2.optional.JDBC4PreparedStatementWrapper.close(JDBC4PreparedStatementWr
apper.java:70)
at com.caucho.sql.UserStatement.close(UserStatement.java:127)
at com.caucho.sql.UserPreparedStatement.close(UserPreparedStatement.java:450)
at org.hibernate.jdbc.AbstractBatcher.closePreparedStatement(AbstractBatcher.java:534)

causata dal bug in questione.

Al fine di risolvere la problematica, in attesa di una nuova release è possibile utilizzare una nightly build del connector j che prevede la correzione del bug.
(nota: quando si reinstalla una libreria jar in glassfish bisogna riavviare).

Io ho risolto con quella del 20 settembre.



No comments: