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;
    }
}


martedì 14 febbraio 2012

Ant esempio utilizzo per Java Project

In questo post vediamo un semplice script Ant che provvede al deploy di un PGM Java in un jar file, stampando a video le proprietà generiche del sistema (JVM,ANT_HOME,Sistema Operativo) e chiedendo prima all'utente se vuole effettuare il deploy o meno; in caso di risposta negativa ant scrive un messaggio di saluto e non procede al deploy (questo solo per vedere un esempio di "if" lato ant).
Il pgm Java è volutamente banale (il classico Hello Word).
Il pgm si trova in un package it.test e si chiama Test.java. Il sorgente è inserito nella nostra base dir dentro la directory src.
Il jar file sarà creato al livello della nostra directory, mentre il .class sarà inserito nella directory build sempre presente a livello della nostra base dir.

Le particolarità di questo script ant sono:

1)  Accesso a variabili di ambiente ( inserendo il nodo <property environment="env"/>) che ci consente poi  di   accedere alla proprietà env direttamente;

2) Utilizzo di proprietà native di ant (disponibili sempre negli script) come ${os.name} per ottenere informazioni sul S.O.  ${ant.java.version} per ottenere la versione di JVM usata, ${user.home} per ottenere la directory user dove di default ant va a cercare ulteriori jar da inserire al classpath oltre a quelli della JVM;

3) Aggiunta di un attributo nel manifest del JAR (in questo caso l'attributo Main-Class) che ci consente di far partire il jar direttamente con java -jar Nome.jar senza specificare l'entry point della classe col main;

4) Gestione della logica condizionale (nel target Opzioni) con l'impostazione di due variabili di uscita (isDeploy e isNotDeploy) che condizionano l'esecuzione dei target di compilazione del sorgente e di creazione del jar file;

Di seguito lo script ant:

<?xml version="1.0" ?>
<project default="main">
<property environment="env"/>
<target name="main" depends="compile,compress,exit">
<echo>Sistema operativo : ${os.name}</echo>
<echo>Versione Java utilizzata: ${ant.java.version}</echo>
<echo>Directory user ${user.home}</echo>
<echo>Ant HOME ${env.ANT_HOME}</echo>
</target>
<target name="compile" depends="Opzioni" if="isDeploy" >
<javac destdir="${basedir}/build" srcdir="${basedir}/src"/>
</target>
<target name="compress" depends="Opzioni" if="isDeploy">
<jar jarfile="Project.jar" basedir="${basedir}/build" includes="**/*.class">
<manifest>
                <attribute name="Main-Class" value="it.test.Test"/>
</manifest>
</jar>
</target>
<target name="Opzioni">
       <input
      message="Si desidera effettuare il deploy ? "
      validargs="y,n"
      addproperty="do.check"
       />
      <condition property="isDeploy">
   <equals arg1="${do.check}" arg2 = "y" />
   </condition>
    <condition property="isNotDeploy">
   <equals arg1="${do.check}" arg2 = "n" />
   </condition>
     </target>

<target name="exit" depends="Opzioni" if="isNotDeploy">
<echo> si e ' scelto di non fare nulla.....</echo>
</target>
</project>


L'output prodotto è il seguente:

C:\proveAnt>ant
Buildfile: C:\proveAnt\build.xml

Opzioni:
    [input] Si desidera effettuare il deploy ?  (y, n)
y

compile:
    [javac] C:\proveAnt\build.xml:11: warning: 'includeantruntime' was not set,
defaulting to build.sysclasspath=last; set to false for repeatable builds

compress:
exit:
main:
     [echo] Sistema operativo : Windows XP
     [echo] Versione Java utilizzata: 1.6
     [echo] Directory user C:\Documents and Settings\sciardone
     [echo] Ant HOME C:\Programmi\apache-ant-1.8.2

BUILD SUCCESSFUL
Total time: 2 seconds

lunedì 13 febbraio 2012

Rimuovere spazi doppi da Stringa

Con le Regular expression basta invocare il metodo replaceAll indicando di effettuare il replace di tutte le sottostringhe composte da uno o più spazi con lo spazio singolo (la regular expression è \\s+ )

String s="test       prova";
System.out.printnl(s.replaceAll("\\s+"," ");

In output avremo "test prova" con un singolo spazio.

Lo stesso risultato si può ottenere (ma è molto meglio usare le Reg Expression) anche con un metodo Java ad hoc tipo questo:

public static String removeSpacesMan(String s){
     StringBuffer sb=new StringBuffer();
     char[] l=s.toCharArray();
     int contaSpazi=0;
     for(int i=0;i<l.length;i++){
     
                   if(l[i]==' '){
      
                                        contaSpazi++;
                                  }
                  else
                                {
                                       contaSpazi=0;
                                }
       if(contaSpazi<=1){
           sb.append(l[i]);
        }
     }
   
     return sb.toString();
    }

lunedì 6 febbraio 2012

Javascript scorporo Iva

Funzioncina per calcolare scorporo iva in javascript, ci sono anche 2 metodi di utilità roundNumber per l'arrotondamento  e parseDouble per effettuare il parsing a Double:

<script language="javascript">
function calcolaScorporo(){

 var iva=parseDouble(document.getElementById("txtIva").value);
  var importoTotale=parseDouble(document.getElementById("txtImporto").value);
 if(!isNaN(iva) && !isNaN(importoTotale)){
 iva=iva/100;
 iva+=1;
 var sc=importoTotale/iva;
 sc=roundNumber(sc,2);
 document.getElementById("txtImportoOriginale").value=sc;
 document.getElementById("txtIvaScorporata").value=roundNumber(importoTotale-sc,2);
 }
 else
 {
    alert('Inserire valori numerici!!');
   }
}


function parseDouble(value){
  if(typeof value == "string") {
    value = value.match(/^-?\d*/)[0];
  }
  return !isNaN(parseInt(value)) ? value * 1 : NaN;
}


function roundNumber(number, digits) {
            var multiple = Math.pow(10, digits);
            var rndedNum = Math.round(number * multiple) / multiple;
            return rndedNum;
        }

</script>


Di seguito vediamo un form che richiama il javascript:



IMPORTO TOTALE PAGATO:
IVA (%):
IVA PAGATA:
IMPORTO ORIGINARIO:

Java encoding URL

Nella costruzione di URL o di link dinamici tramite concatenazione di Stringhe è facile incappare in errori di encoding dovuti a caratteri particolari, i cosiddetti caratteri "unsafe".
Per ovviare a questo problema la JDK ci viene in soccorso con la comoda classe java.net.URLEncoder.
Un esempio di utilizzo all'interno di un proprio metodo  è incollato sotto.
Il metodo encode di URLEncoder lancia una checked Exception, UnsupportedEncodingException, che è necessario gestire .
Per verificare se la propria JDK supporta UTF-8 come dovrebbe basta eseguire il seguente codice:

java.nio.charset.Charset.forName("UTF-8");

Se la chiamata al metodo va in eccezione allora l'encoding non è supportato dalla JDK.

Esempio metodo:

public static String encode(String s) {
        String retVal="";
        boolean supportUTF8=true;
        try
        {
           
            retVal=URLEncoder.encode(s, "UTF-8");
        }
        catch(UnsupportedEncodingException ex){
            // da specifica
            //The World Wide Web Consortium Recommendation states that UTF-8 should be used.
            // Not doing so may introduce incompatibilites.
            log.error("Attenzione, il runtime Java non supporta la codifica UTF-8 per l'url encoding, tentiamo la replace manuale dei caratteri particolari");
            log.error("Verificare il tipo di JDK usata, UTF-8 dovrebbe sempre essere supportato");
            supportUTF8=false;
        }
        if(!supportUTF8){
            retVal=provaAMano();// implementarsi questo metodo tentando di fare qualcosa manualmente, altrimenti tira su un'eccezione
        }
        return retVal;
    }

venerdì 3 febbraio 2012

Java Sql dinamico immune da SQL Injection

Uno dei problemi frequenti con l'Sql dinamico è il rischio di SQL Injection.
Infatti spesso si tende a costruire le query dinamiche semplicemente concatenando le stringhe con i parametri.
Vediamo come realizzare l'SQL dinamico utilizzando una web app.
L'esempio è realizzato usando semplicemente JDBC, ma si può in maniera analoga estendere a JPA.
Il concetto base è che per evitare SQL Injection bisogna utilizzare i PreparedStatement, quindi in generale non concatenando stringhe ma utilizzando i placeholder (in JDBC e in JPA si usa il ?), rendendo quindi la query dinamica e non statica come con la semplice concatenazione dei parametri.

Vediamo un esempio molto semplice, abbiamo un form con 3 campi di ricerca, Nome Cognome ed Età e vogliamo costruire la nostra query dinamica.
La stringa della Query in questo semplice caso è una costante presente in una classe, ed è così definita:

public static String GET_ANAGRAFICA="select * from persona where 1=1 /*1 " +
            "and nome like ? 1*/ /*2 and cognome like ?2*/ " +
            " /*3 and eta=? 3*/";


Come possiamo notare i parametri sono commentati (/*1....1*/).

Lato Web sono impostati i parametri nelle form e quindi si effettua l'invio ad una Servlet che si occupa di evadere la richiesta (non posto il codice che è banale).

Quello che avviene a questo punto nel metodo che si occupa nel Back End di ritornare i dati all'utente è il seguente:


public List<Persona> getListaCompletaPersone(Persona p){
        List<Persona> lista=new ArrayList<Persona>();
        try
        {
   
        PreparedStatement   pst=getDs().getConnection().prepareStatement(lavoraQuery(p));
        pst.setString(1, "%"+p.getNome()+"%");
        pst.setString(2,"%"+ p.getCognome()+"%");
        pst.setInt(3, p.getEta());
       
        ResultSet rs=pst.executeQuery();
        while(rs.next()){
            Persona pp=new Persona();
            pp.setCognome(rs.getString("cognome"));
            pp.setEta(rs.getInt("eta"));
            pp.setNome(rs.getString("nome"));
            lista.add(pp);
        }
        pst.close();
        rs.close();
       
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
        return lista;
    }


Il metodo chiave per la creazione della stringa è il lavoraQuery che  nel caso in cui sia presente uno o più dei parametri toglie i commenti relativi, rendendo quindi la selezione effettiva.


private String lavoraQuery(Persona p){
        String qry=Query.GET_ANAGRAFICA;
        if(p.getCognome()!=null && !p.getCognome().trim().equals("")){
            qry=qry.replace("/*2", "");
            qry=qry.replace("2*/", "");
           
        }
        if(p.getNome()!=null && !p.getNome().trim().equals("")){
            qry=qry.replace("/*1", "");
            qry=qry.replace("1*/", "");
           
        }
        if(p.getEta()!=0){
            qry=qry.replace("/*3", "");
            qry=qry.replace("3*/", "");
        }
       
        return qry;
    }

Java connessione via datasource con JBOSS

Per ottenere la connessione via Java con una webapp si devono effettuare 4 operazioni:
1) Definire un datasource dentro JBOSS;
2) Definire il nodo resource-ref nel web.xml;
3) Definire il file jboss-web.xml dentro la directory WEB-INF;
4) Effettuare la lookup al datasource

Di seguito vediamo i 4 passi in pratica, avendo come AS JBOSS 5.1 e come database sottostante MySql 5.1.

Passo 1 

Il datasource XXXXX-ds.xml

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
    <jndi-name>jdbc/personaDS</jndi-name>
    <connection-url>jdbc:mysql://localhost:3306/gestore</connection-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <user-name>root</user-name>
    <password>root</password>
    <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
    <!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml (optional) -->
    <metadata>
       <type-mapping>mySQL</type-mapping>
    </metadata>
  </local-tx-datasource>
</datasources>


Passo 2

Nel web.xml abbiamo :

 <resource-ref>
    <description>DB Connection</description>
    <res-ref-name>jdbc/personaDS</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>


Passo 3

Il file jboss-web.xml

<jboss-web>
  <context-root>GestorePersonaWeb</context-root>
  <resource-ref>
      <description>database connection</description>
      <res-ref-name>jdbc/personaDS</res-ref-name>
      <jndi-name>java:jdbc/personaDS</jndi-name>
      </resource-ref>
</jboss-web> 





Passo 4


Per ottenere la reference al datasource si utilizzano le seguenti istruzioni:
.......
Context initContext = new InitialContext();
Context envContext  = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/personaDS");
.....