mercoledì 15 febbraio 2012

EJB Timer esempio di utilizzo


Una delle caratteristiche avanzate degli Ejb 3.0 è la possibilità di definire dei Timer sull'AS.

TEORIA

I Timer consentono di effettuare operazioni schedulate.
Essendo asincroni e stateless possono essere implementati soltanto con i Session Beans e con i Message Driven Bean.
I Timers sono anche transazionali, quindi se il metodo che li utilizza va in eccezione anche il Timer viene distrutto.
Altro vantaggio dei Timer è che, una volta creati, sopravvivono allo spegnimento dell'AS, quindi al riavvio saranno di nuovo attivi.
Per crearli si utilizza l'interfaccia TimerService, che va iniettata a livello di bean.
L'interfaccia javax.ejb.TimerService è così definita:

public interface javax.ejb.TimerService {
public Timer createTimer(long duration,
java.io.Serializable info);
public Timer createTimer(long initialDuration,
long intervalDuration, java.io.Serializable info);
public Timer createTimer(java.util.Date expiration,
java.io.Serializable info);
public Timer createTimer(java.util.Date initialExpiration,
long intervalDuration, java.io.Serializable info);
public Collection getTimers();
}

E' possibile quindi definire delle schedulazioni base, dando la data iniziale e specificando ogni quanto saranno ripetuti oppure facendoli partire una singola volta soltanto. Non c'è ovviamente la completezza di un cron scheduler o di tool come Quartz, ma in molti casi risultano utili.
Il "cosa fare" alla scadenza del timer è indicato nell'ejb annotando un metodo con l'annotazione @TimeOut.
Il metodo deve avere una definizione di questo tipo:

void <METHOD>(Timer timer)

L'interfaccia javax.ejb.Timer è così definita:

 public interface javax.ejb.Timer {
public void cancel();
public long getTimeRemaining();
public java.util.Date getNextTimeout();
public javax.ejb.TimerHandle getHandle();
public java.io.Serializable getInfo();
}
Presenta una serie di metodi di utilità ; molto importante è il metodo cancel() che consente di rimuovere il Timer dall'AS.

ESEMPIO DI UTILIZZO

Vediamo un semplice esempio in cui abbiamo un Ejb remoto che in un suo metodo crea il Timer.

Questo Timer nel metodo Timeout effettua delle insert su una tabella di DataBase, ripetendo questi inserimenti ogni minuto.

Il db server di riferimento è MySql 5.1., l'AS JBoss 5.1 la jdk utilizzata la java 1.6.x.
Come ambiente di sviluppo ho usato Eclipse Indigo, creando un semplice progetto Java e aggiungendo nel classpath le librerie dell'Application Server. 
Per deployare l'Ejb è sufficiente effettuare l'export del jar dentro la directory deploy di Jboss (si noti che a aprtire dalla versione 3.0  il file ejb-jar.xml non è più necessario)
Essendoci la connettività verso MySql è necessario scaricare dal sito il driver JDBC e copiarlo dentro la directory server/default/lib.

La tabella creata ha la seguente struttura:

CREATE TABLE  `test`.`logaccessi` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `descrizione` varchar(45) NOT NULL,
  `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;

L'Entity Bean utilizzato è il seguente:

@Entity
@Table(name="logaccessi")
public class LogInfo implements Serializable{
   
    /**
     *
     */
    private static final long serialVersionUID = 1L;

    private int id;
   
    private String descrizione;
   
    private Timestamp ts;
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Column(name="descrizione")
    public String getDescrizione() {
        return descrizione;
    }
    public void setDescrizione(String descrizione) {
        this.descrizione = descrizione;
    }
    @Column(name="ts")
    public Timestamp getTs() {
        return ts;
    }
    public void setTs(Timestamp ts) {
        this.ts = ts;
    }
   
   

}


Il file persistence.xml definito dentro la directory META-INF del progetto java è il seguente:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">

    <persistence-unit name="timer" transaction-type="JTA">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:jdbc/logDS</jta-data-source>
        <properties>
         <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect"/>
        <property name="hibernate.show_sql" value="true"/>
        <property name="hibernate.format_sql" value="true"/>
        </properties>
    </persistence-unit>
   
</persistence>



Il datasource sotto Jboss:

<datasources>
  <local-tx-datasource>
 
    <jndi-name>jdbc/logDS</jndi-name>
 
    <connection-url>jdbc:mysql://localhost:3306/test</connection-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
 
    <user-name>root</user-name>
    <password>admin</password>
 
    <connection-property name="autoReconnect">true</connection-property>
 
    <!-- Typemapping for JBoss 4.0 -->
    <metadata>
      <type-mapping>mySQL</type-mapping>
    </metadata>
 
  </local-tx-datasource>
</datasources>

L'interfaccia Remota dell'Ejb:

@Remote
public interface LocalTimer {
           //starta il timer
    public void start();
           // rimuove tutti i Timer presenti nell'AS
    public void removeTimers();
     
}

L'Ejb Stateless:

@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class LocalTimerEjb implements LocalTimer {

    @PersistenceContext(unitName = "timer")
    private EntityManager entityManager;
    @Resource
    TimerService  timer;
   
    public void start() {
       
        timer.createTimer(new Date(), 1*60*1000,new TimerInfo());
        System.out.println("Timer creato.....");
    }
    @Remove
    public void stop(){
        removeTimers();
    }
    @Timeout
    public void lavora(Timer timer){
       
        System.out.println("In esecuzione il timer creato il "+((TimerInfo)timer.getInfo()).getDataCreazione());
        LogInfo l=new LogInfo();
        l.setDescrizione(generaTestoCasuale(16));
        l.setTs(new java.sql.Timestamp(System.currentTimeMillis()));
        entityManager.persist(l);
        System.out.println("Finito!!!");
    }
    private String generaTestoCasuale(int lunghezza){
        .......
    }
   
    @Override
    public void removeTimers() {
        Collection<Timer> lista=timer.getTimers();
        int numero=lista.size();
        for(Timer t:lista){
            t.cancel();
        }
        System.out.println("Timer cancellati: "+numero);
       
    }
}

L'oggetto TimerInfo() passato al metodo create Timer da' solo informazioni sul Timer creato (in questo esempio volutamente banale soltanto la data creazione).

public class TimerInfo implements Serializable {
    public TimerInfo(){
        this.dataCreazione=new Date();
    }
    private static final long serialVersionUID = 1L;
   
    private Date dataCreazione;

    public Date getDataCreazione() {
        return dataCreazione;
    }

    public void setDataCreazione(Date dataCreazione) {
        this.dataCreazione = dataCreazione;
    }
   
}




Per Invocare l'Ejb, in un progetto client:

public class StartEjbTimer {

   
    public static void main(String[] args) throws Exception {
       
       
        LocalTimer lt=(LocalTimer)getContesto().lookup("LocalTimerEjb/remote");
        lt.removeTimers();
        lt.start();
      

    }
    public static Context getContesto() throws NamingException{
        Properties p=new Properties();
        p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
        p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
        p.put(Context.PROVIDER_URL, "jnp://127.0.0.1:1099");
        Context ctx=new InitialContext(p);
        return ctx;
    }
}


Nessun commento:

Posta un commento