Posts tagged: java

Spring Web Flow tutto d’un fiato ! [Part 1/2]

spring-webflowSpring Web Flow è un framework basato su Spring MVC che permette lo sviluppo di applicazioni flow-based. In questo articolo vedremo come aggiungere Spring Web Flow ad applicazioni Spring-based e definire alcuni flussi tra le applicazioni e i loro utenti.
Iniziamo col dire che tutti i flussi (da ora in avanti li chiameremo con loro vero nome, flow) sono costituiti da 3 elementi base, ovvero:

  • States
  • Transitions
  • Flow data

States: indica ciò che accade all’interno di un flow. Spring Web Flow definisce 5 tipi di States differenti: View, Active, End, Subflow, Decision. Vedremo più avanti come questi tipi di stato partecipano alla creazione di un flow.
Transitions: sono di fatto il mezzo di collegamento tra i vari States. Una View state, può contenere al suo interno un certo numero di transizioni ciascuna delle quali collega altri stati.
Flow data: all’interno di un flow vengono collezionati alcuni dati che dipendono dagli stati in cui il flow si trova o si trovava. La visibilità dei dati di ciascun flow dipende dallo scope con cui questo viene definito. Per la precisione esistono 5 tipi di scope differenti in SWF che sono:
 

  • Flow scope : viene creato all’inizio del flow e distrutto quando il flow termina. I dati all’interno del Flow scope sono accessibili e disponibili per tutta la durata del flow
  • Conversation scope : viene creato all’inizio di un top-level flow e distrutto quanto il flow termina. Il conversation scope è simile al flow scope, eccetto per l’accesso ai dati. Infatti mentre il flow scope è accessibile solo dal flow che l’ha creato, il conversation flow può essere acceduto sia dal top-level flow che da tutti i suoi subflow.
  • Request scope : viene creato all’inizio di una richiesta HTTP e distrutto al termine di tale richiesta. I dati all’interno del request scope sono disponibili a tutti gli stati del flow per l’intera durata della richiesta.
  • Flash scope : viene creato quando il flow ha inizio, ripulito quando si entra all’interno di una view e distrutto alla fine del flow. I dati all’interno del flash scope sono disponibili a tutti gli states per l’intera durata del flow.
  • View scope : viene creato quando il flow entra all’interno di una view e distrutto quando la view viene renderizzata. E’ il flow che ha più breve durata, per questo motivo i dati all’interno di questo scope sono disponibili solo alla view che li ha creati.

Ciò detto vediamo come fare ad installare ed utilizzare SFW in pochi semplice passi. Naturalmente per poter iniziare a lavorare con SWF è necessario scaricare i jar che lo contengono, questa pagina costituisce il punto di partenza da cui potete trovare info dettagliate sul framework e dal link download potete scaricare le librerie. Le librerie necessario da aggiungere al classpath della propria applicazione sono :

  • org.springframework.WebFlow-2.0.8.RELEASE.jar
  • org.springframework.binding-2.0.8.RELEASE.jar
  • org.springframework.faces-2.0.8.RELEASE.jar
  • org.springframework.js-2.0.8.RELEASE.jar

in realtà WebFlow e Binding sono le librerie principali, mentre faces e js mettono a disposizione il supporto a JSF e Javascript/Ajax. In aggiunta a queste librerie è necessario aggiungere il supporto al linguaggio che aiuta a definire i Flow.
SWF usa OGNL.

SWF come detto si basa su Spring MVC, quindi così come per tutte le applicazioni spring-based, le richieste passano attraverso il DispatcherServlet. Per configurare SWF basta aggiungere le seguenti righe di codice al file servlet.xml :

<servlet>
    <servlet-name>DevMeApp</servlet-name>   
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

Bene, a questo punto non rimane che indicare quale richiesta debba essere "catturata" da spring e quindi girata a SWF. Per fare questo, ci basterà configurare il nostro DispatcherServlet con un handle per tutte le richieste il cui URL fa match con la stringa "devme"…tradotto:

<servlet-mapping>
  <servlet-name>DevMeApp</servlet-name>
  <url-pattern>/devme/*</url-pattern>
</servlet-mapping>

Ora che abbiamo configurato Spring passiamo alla configurazione di SWF. La configurazione avviene tramite compilazione di file XML di Spring aggiungendo a tali file il namespace di SWF, in modo tale da poter utilizzare i tag messi a disposizione dal framework. Quindi partendo da un file di spring aggiungiamo i seguenti namespace:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:webflow="http://www.springframework.org/schema/webflow-config"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/webflow-config
           http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
</beans>

quindi aggiungiamo la nostra configurazione all’interno del tag beans.
La prima cosa da fare è aggiungere un riferimento al flow executor, ovvero all’elemento che gestisce l’esecuzione del flow. Aggiungiamo quindi il tag:

<webflow:flow-executor id="flowExecutor"/>

Il secondo step è quello di indicare a SWF dove trovare il file xml della definizione del flow. Questo viene fatto attraverso il tag flow registry che permette di indicare la posizione su file system in cui risiedono i file XML dei vari flow. Per esperienza, è molto comodo creare i file di flusso separati all’interno di ogni singolo file.
Aggiungiamo quindi il flow registry:

<webflow:flow-registry id="flowRegistry" base-path="/WEB-INF/flows-defs">
	<webflow:flow-location id="devme" path="/WEB-INF/flows/devme-flow.xml" />
</webflow:flow-registry>

l’attributo id del tag flow location permette di assegnare un id al flow di modo che possa essere riferito all’occorrenza. In assenza dell’attributi id, l’id del flow corrisponderà al nome del file del flow.

SWF fornisce un handler adapter (spring based) che permette che fa da bridge tra il DispatcherServlet e il flow executor, gestendo la richiesta e manipolando il flow. E’ necessario configurare l’handler all’interno del file di configurazione affinché possa essere invocato dal framework. Aggiungiamo quindi il seguente tag bean:

<bean class="org.springframework.WebFlow.mvc.servlet.FlowHandlerAdapter">
  <property name="flowExecutor" ref="flowExecutor" />
</bean>
<bean class="org.springframework.WebFlow.mvc.servlet.FlowHandlerMapping">
  <property name="flowRegistry" ref="flowRegistry" />
</bean>

in questo modo il DispatcherServlet è in grado di stabilire a chi girare la richesta consultando uno o più handler. Nel codice di sopra abbiamo aggiunto un riferimento al FlowHandlerMapping il quale mantiene un riferimento al FlowRegistry in modo tale da girare la richiesta al flow corretto. Un’applicazione basata su SWF tipicamente contiene più di un flow, per questo il collegamento tra FlowHandlerMapping e flow registry consente di selezionare il flow corretto sulla base della richiesta ricevuta.

Dopo aver configurato la nostra applicazione Spring fornendo il supporto a SWF vediamo come definire un file di flow. Un file di flow è un file XML diverso da quello visti finora, nel senso che ha un nodo radice diverso il quale appartiene ad un namespace differente.
 

<flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow
      http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
	<-- Add your definition here -->
</flow>

Vediamo quali sono gli elementi che contribuiscono alla definizione di un flow. Di seguito viene mostrato un elenco di tali elementi con per ciascuno una breve descrizione.

  • <action-state>, definisce una o più azioni, al termine delle quali si può transire verso uno stato successivo all’interno del flow.
  • <attribute>, consente di definire un attributo da memorizzare all’interno del flow. Viene utilizzato assieme al tag <value>.
  • <bean-import>, importa un elemento bean definito dall’utente.
  • <decision-state>, permette di valutare un’espressione sulla base della quale decidere verso quale stato transire.
  • <end-state>, indica lo stato finale del flow. La transizione su questo stato comporta il termine del flow.
  • <exception-handler>, indica un bean che può gestire eccezioni per questo flow.
  • <global-transition>, definisce una o più transizioni che sono disponibili da tutti gli stati.
  • <input>, definisce un input per questo flow.
  • <on-end>, evento che definisce l’azione che viene richiamata quando il flow termina.
  • <on-start>, evento che definisce l’azione che viene richiamata quando il flow inizia.
  • <output>, definisce un output per questo flow.
  • <persistence-context>, crea e alloca un contesto quando il flow ha inizio. Viene usato assieme al transaction manager.
  • <secured>, viene usato per restringere l’accesso ad un determinato stato.
  • <subflow-state>, invoca un nuovo flow come sotto-flow di quello corrente.
  • <var>, definisce una variabile con flow scope.
  • <view-state>, indica uno stato che si presenta all’utente tipicamente caricando un certo output e richiedendo l’interazione da parte dell’utente.

Per evitare di dlungarmi più di quanto abbia già fatto rimando la continuazione di questo articolo al post successivo. Continuo a scrivere quandi non passerà molto dalla pubblicazione seguente…prometto :P  !
Stay tuned.

NFC: qualcosa si muove (part-two)

NFC2Seconda parte dell’articolo su come scrivere programma che legge da un tag RFC e scrive su un tag RFC. Qui potete trovare quanto scritto sulla lettura da un tag NFC.
La parte che andremo ad analizzare prevede la scrittura di alcune informazioni su un tag NFC, attraverso l’interfaccia della nostra applicazione. Nello specifico andremo a scrivere i dati di un nuovo contatto della rubrica sul tag e successivamente potremo leggere quanto scritto con la funzionalità vista nell’articolo precedente.

Anche in questo caso la logica che sta dietro alla funzionalità è basata sugli eventi, in particolare, per la classe NFCWriter viene registrato un evento, il Tag Detection, che quanto catturato, esegue il codice contenuto al suo interno. Dall’interfaccia viene richiesto di inserire il testo da scrivere sul tag, si preme su OK e si avvicina il cellulare in prossimità del tag. A quel punto viene gestito l’evento che si preoccupa di stabilire una connessione con il tag NFC ed inviare.

 

write-on-nfc-1     write-on-nfc-3

 

Vediamo il codice che realizza quanto detto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class NDEFWriter extends NDEFHandler {
    Form previousScreen = null;
    String fName = "No name";
 
    public NDEFWriter(Form form) {
	previousScreen = form;
    }	
 
    public void targetDetectionMade(TargetProperties[] tProp) {
	previousScreen.append("Target detected.\n");
	if(tProp == null || tProp.length == 0) {
	    previousScreen.append("No TargetProperties found!\n");
	}
	NDEFTagConnection ndefTagConnection = null;
	try {				
	    previousScreen.append("Target mapping:"+tProp[0].getMapping()+"\n");				
	    ndefTagConnection =NDEFUtil.getNDEFTAGConnection(tProp,previousScreen);
	    if(ndefTagConnection == null) {
		previousScreen.append("Could not establish connection to card.\n");
		return;
	    }
	    NDEFUtil.writeNDEFMessage(ndefTagConnection,previousScreen);
	    ndefTagConnection.close();
	} catch (Exception e) {
	    if(ndefTagConnection != null) {
		try {
		    ndefTagConnection.close();
		} catch (IOException e1) {
		    previousScreen.append("Exception in closing connection:"+e1.toString());
		}
	    }
	    previousScreen.append("Exception:\n"+e.toString()+"\n");
	    e.printStackTrace();
	}
 
    }
 
    public String getFormattedName() {
	return fName;
    }
 
    public void setFormattedName(String fName) {
	this.fName = fName;	
    }
}

Verifica dei dati scritti sul tag

Come potete vedere non è molto difficile realizzare un programma Java che sfrutti le potenzialità fornite dalla tecnologia NFC, tutta sta a conoscere un pò le API messe a disposizione dalla Nokia. Tutto il resto è Java, gestione degli eventi, ereditarietà, interfacce, etc etc. Un vero (buon) programmatore Java non troverà alcuna difficoltà a realizzare la maggior parte delle funzionalità.
Questo è tutto.
Il codice completo per far girare l’esempio lo trovato nelle SDK del Nokia 6131 NFC è necessario la registrazione sul sito per poter effettuare il download. Dopo di che cercate nella cartella examples.
Stay tuned. yo.
 

NFC: qualcosa si muove (part-one)

NFC2Oggi ho visto in tv un servizio che parlava su come usare il proprio cellulare per effettuare piccoli pagamenti, il cosidetto telefonino/bancomat. Il funzionamento si basa sulla tecnologia Near Field Communication, ovvero un’evoluzione dell’ RFID la tecnologia che permette di far comunicare due dispositivi portanto in prossimità l’uno dell’altro. In questo articolo pubblicato sulle pagine di devme poco tempo fa, reperibile qui si parla di questa tecnologia, in particolare è possibile utilizzare il telefonino come fosse bancomato, nella modalità touch-to-pay: "avvicinando il cellulare sarà possibile utilizzarlo come una carta di credito. Le informazioni sensibili della carta sono memorizzate al suo interno in un elemento chiamato secure element. In sostanza funziona come una carta di credito senza contatto.E’ possibile visualizzare le informazioni informative classiche delle proprie transazioni".
Nello specifico in linea di principio basta scrivere i dati del proprio bancomat all’interno del cosidetto secure element e a quel punto sarà possibile, avvicinando il cellulare in prossimità di un dispositivo capace di leggere tag NFC, effettuare un’addebito sul proprio conto corrente. Pensateci, quale migliore comodità !!
Bene, analizziamo ora più da vicino, in un esempio molto semplice, come scrivere un programma che legge da un tag NFC e scrive su un tag NFC. Il programma è scritto in Java, l’IDE che utilizziamo per fare il test è Eclipse sul quale installeremo il plugin eclipseme che permette di sviluppare applicazione utilizzando le librerie J2ME.

Partiamo quindi lo scrivere la nostra midlet, DevMeNFCMidlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class DevMeNFCMidlet extends MIDlet {
    Display display;
    MainList list;
    String[] actions = new String[]{"Read","Write","Show contacts"};
    Image[] images = null;
 
    protected void startApp() throws MIDletStateChangeException {
	display = Display.getDisplay(this);
	list = new MainList("Select action to do:", List.EXCLUSIVE,actions,images,this,null);
	display.setCurrent(list);
    }
 
    protected void pauseApp() {}
 
    public void destroyApp(boolean arg0) throws MIDletStateChangeException {
	notifyDestroyed();
    }
 
    /**
     * Helper method to change the current displayable
     * 
    */
    public void setToDisplay(Displayable dspl) {
	display.setCurrent(dspl);
    }
}

Nel caso vi foste persi qualcosa sulle Midlet Java, in breve, è un’applicazione che gira su sistemi su cui è presente la J2ME Virtual Machine. Per creare una Midlet è sufficente estendere la propria classe con la classe Midlet, messa a disposizione dal Wireless Toolkit. L’estensione impone l’implementazione di alcuni metodi dal momento che la classe Midlet è una classe astratta. Il metodo StartApp() viene richiamato al momento del lancio dell’applicazione e nel nostro caso crea una lista con 3 opzioni: Read, Write e Show Contacts. La nostra applicazione legge da un tag NFC virtuale, emulato per intenderci e scrive sullo stesso tag. L’emulazione viene messa a disposizione dalle librerie opzionali, Contactless Communication API scaricabili dal sito della nokia, che sarà dunque necessario rendere disponibili all’interno del progetto in eclipse. Lanciando l’applicazione avremo:

midlet-on-nfc

Ciò che si vede è l’interfaccia dell’emulatore con la midlet caricata e pronta per essere eseguita. L’emulatore sostanzialmente è un cellulare, nello specifico il nokia 6131 che possiede al suo interno un tag NFC con le relative librerie. Per avviare l’applicazione è sufficente cliccare il bottone (sul cellulare) in corrispondenza della voce Select.

midlet3-on-nfcL’applicazione come detto mostra il menù con 3 voce selezionabili, in particolare se selezioniamo l’opzione Read saremo in grado di leggere da un tag NFC un certo contenuto. Nella nostra simulazione, i tag NFC sono virtuali e vengono mostrati sulla destra nell’interfaccia dell’emulatore. Alcuni di questi sono attivi, connessi altri disconnessi. Per effettuare una simulazione di lettura trasciniamo il tag NFC attivo all’interno del cellulare come mostrato in figura. A quel punto il contenuto del tag viene mostrato all’interno del telefono.

Ma diamo un occhiata al codice che realizza la funzionalità. L’idea che sta dietro alla logica è molto semplice. Si registra la classe del Reader per un determinato evento, ovvero per l’evento Tag Detection. Rilevato il tag viene richiamato un metodo della classe che stabilisce una connessione con il tag e legge il messaggio inviato dal tag. Il contenuto del messagio viene visualizzato sul display del cellulare. Di seguito incollo il codice della classe Reader.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class NDEFReader extends NDEFHandler {
    Form previousScreen = null;
    public NDEFReader(Form form) {
        super();
        previousScreen = form;
    }
 
    public void targetDetectionMade(TargetProperties[] tProp) {
	previousScreen.append("Target detected.\n");
	if(tProp == null || tProp.length == 0) {
	    previousScreen.append("No TargetProperties found!\n");
	}
	NDEFTagConnection ndefConnection = null;
	try {			
	    previousScreen.append("Target mapping:"+tProp[0].getMapping()+"\n");				
	    ndefConnection =NDEFUtil.getNDEFTAGConnection(tProp,previousScreen);
	    if(ndefConnection == null) {
		previousScreen.append("Could not establish connection to card.\n");
		return;
	    }
	    NDEFUtil.readNDEFMessage(ndefConnection,previousScreen);
	    ndefConnection.close();
	} catch (Exception e) {
	    if(ndefConnection != null) {
	        try {
		    ndefConnection.close();
	        } catch (IOException e1) {
		    previousScreen.append("Exception in closing connection:"+e1.toString());
	        }
	    }
	    previousScreen.append("Exception:\n"+e.toString()+"\n");
	    e.printStackTrace();
        }
    }
 
    public String getFormattedName() {
        return null;
    }
 
    public void setFormattedName(String fName) {}
 
}

Il metodo targetDetectionMade realizza tutto quello che è stato detto nel paragrafo precedente. Per quanto riguarda il resto del codice rimando ai prossimi articoli. Stay tuned! yo

Crittare l’url di un immagine in jsp con Spring

DataObfuscationIn questo articolo voglio far vedere come nascondere l’href di un immagine in una pagine JSP, pur visualizzando l’immagine come elemento HTML in modo standard.
Supponiamo di dover sviluppare un’applicazione web in cui ciascun utente mantiene una libreria multimediale, costituita per semplicità da soli elementi immagine. Ogni immagine viene memorizzata all’interno di un database assieme alla relative informazioni dell’utente a cui appartiene.
Affinché l’immagine non sia accessibile semplicemente attraverso l’indirizzo indicato nell’HREF, è necessario offuscare il suo contenuto attraverso una codifica il cui funzionamento è noto al sistema. Lo stesso sistema sarà in grado così di decodificare l’HREF in modo da poter restituire la risorsa e quindi visulalizzarla all’interno della pagina.
Vediamo il seguente esempio:

1
2
3
4
5
   <!-- Immagine in chiaro -->
   <img src="http://host/images/img.jpg" title="Immgine in chiaro" alt="" />
 
   <!-- Immagine offuscata -->
   <img src="http://host/image.htm?a=QVMyJCU0c2Rm%3D&b=QVMyJCU0c2RmMzI0M3NkdzI%3D" title="Immgine in chiaro" alt="" />

Nell’esempio di sopra viene mostrata un’immagine in cui l’href è crittata secondo un algoritmo che ora vedremo. E’ chiaro che nel secondo caso, affinché l’immagine possa essere visualizzata è necessaria la presenza di un elemento capace di interpretarne il contenuto, ottenere e restituire la risorsa.
Nell’ambito di Spring tutto questo può essere tradotto nel modo seguente: è necessario un Controller che venga richiamato ogni volta in cui nella pagina è presente un’immagine.
Il Controller dovrà prelevare il contenuto dell’HREF decodificarlo e restituire l’immagine, se corretto. Procediamo quindi per step.
Primo Step configuriamo il Controller (Servlet) all’interno di Spring…quindi editiamo il file di configurazione di spring ed aggiungiamo le seguenti righe di codice:

1
2
3
4
5
6
7
8
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" id="urlMapping"> 
        <property name="mappings">
	<value>/image.htm=mediaResolver</value>
        <property value="true" name="alwaysUseFullPath"></property>
    </bean> 
    <bean class="it.devme.obfuscator.media.MediaResolver" id="mediaResolver">
        <property name="dbManager"><ref bean="dbManager"></ref></property>     
    </bean>

L’invocazione della risorsa virtuale index.htm provoca l’invocazione del Controller MediaResolver. Vediamo la sua struttura di seguito:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MediaResolver extends BaseCommandController {
 
    private DBManager dbManager;
    public void setDbManager (DBManager dbManager) {
	this.dbManager = dbManager;
    }
 
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	Object mediaid = req.getParameter("a");
	Object uid = req.getParameter("b");
 
	if (mediaid==null || uid==null) 
	    return new ModelAndView(new MediaView("image/jpeg", "no-image.jpg", rs.getString("basePath"), MediaView.USE_DEFAULT_IMAGE));
 
	Encrypter dec = Encrypter.getInstance();
	int idaccount = Integer.parseInt(URLDecoder.decode(dec.decrypt(uid.toString()), "UTF-8"));
	int idelement = Integer.parseInt(URLDecoder.decode(dec.decrypt(mediaid.toString()), "UTF-8"));
 
	MediaBean media = null;
	File f = null;
	String mediaPath = null;
 
	/* Check if media item exists */
	media = dbManager.getImage(idelement, idaccount);
	if (media==null) return new ModelAndView();
	mediaPath = media.getPath();
	f = new File(UPLOAD_PATH + File.separator + mediaPath);
 
	MimetypesFileTypeMap mime = new MimetypesFileTypeMap();
 
	resp.setHeader("Cache-Control", "private,no-cache,no-store");
	resp.setContentType(mime.getContentType(f));
	return new ModelAndView(new MediaView(mime.getContentType(f), mediaPath, rs.getString("basePath"), !MediaView.USE_DEFAULT_IMAGE));
    }
 
}

La classe di sopra estende BaseCommandController e quando invocata viene richiamato l’unico metodo di cui è stato fatto l’override che si occupa di decodificare e restituire la risorsa. Vediamo come.
Sostanzialmente recupera i parametri dalla GET (nel nostro caso sono a e b). Li decodifica utilizzando un algoritmo di crittografia a noi noto (si può usare qualunque algoritmo a nostro piacimento basato su una chiave provata) e quindi ottiene per il parametro a, l’id dell’utente a cui appartiene la risorsa e per il parametro b, l’id della risorsa a cui si vuole accedere. Questi dati vengono passati in input ad un metodo che effettua una query su db (getImage(idelement, idaccount)) e verifica se effettivamente la risorsa richiesta appartiene all’utente corrente.
In caso contrario viene restituita una View vuota, altrimenti si determina il mime-type della risorsa e si restituisce una view creata a partire dalla classe MediaView.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class MediaView extends AbstractView {
 
    private String media;
    private String basePath;
    private boolean noImage;
 
    public MediaView(String mime, String media, String basePath, boolean noImage) {
	this.media = media; 
	setContentType(mime);
	this.basePath = basePath;
    }
 
    @Override
    protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
	String rpath = 	null;
 
	rpath = this.basePath + request.getContextPath().substring(1) +
		MediaResolver.UPLOAD_PATH + File.separator + this.media;
 
 
	File f = new File(rpath);
	byte[] bytes = FileUtils.readFileToByteArray(f);
 
 
        // Write content type and also length (determined via byte array).
        response.setContentType(getContentType());
        response.setContentLength(bytes.length);
 
        // Flush byte array to servlet output stream.
        ServletOutputStream out = response.getOutputStream();
        out.write(bytes);
        out.flush();
    }
 
}

La classe MediaView non fa altro che leggere fisicamente i byte dell’immagine e restituirla nella response utilizzando un OutputStream. Il risultato è che l’immagine scritta nella response verrà visualizzata in pagina come un elemento HTML standard, mantenendo perà l’HREF dell’immagine crittato.
Alla prossima.

X-stream: xml è facile !

xml_at_workSalve a tutti…ebbene sì…si ricomincia. Siccome è passato un pò di tempo dall’ultimo post, inizierò a scrivere così come se niente fosse…così nessuno se né accorgerà :P  ! Sono alle prese con lo sviluppo di un’applicazione web, che fa uso di XML per la serializzazione dei dati. L’applicazione è scritta in Java, usa Spring come framework di appoggio. La parte fondamentale come dicevo, consiste nella serializzazione dei dati in XML. La struttura XML che deve essere serializzata non è molto complessa, ma neanche troppo semplice…per cui al primo impatto, ho deciso di non appoggiarmi sulle classiche librerie messe a disposizione da Java (xerces) o quelle presenti di default nelle API (javax.xml.parser).
Sono andato così alla ricerca in rete di qualcosa che potesse essermi utile, cercavo qualcosa che fosse facile da usare e al tempo stesso leggero, in modo tale da non appesantire l’applicazione. Ho trovato così XStream, una libreria che permette di serializzare oggetti in XML e viceversa. Il viceversa nel mio caso è fondamentale, dal momento che se possibile inizializzare una struttura dati di oggetti senza dover, materialmente, navigare l’XML mi ha aiutato molto.
Non mi soffermerò molto nel descrivere le caratteristiche della libreria, sopra come avrete notato ho linkato l’URL quindi potete fare da voi…mi concentrerò su qualche feature che mi è sembrata interessante e descriverò qualche esempio pratico.
La creazione di un file XML a partire da un oggetto Java è molto semplice, si parte dalla creazione e inizializzazione dell’oggetto stesso, dopo di ché si procedere richiamando un metodo statico che provvede alla generazione (serializzazione) del file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Blog {
  private String name;
  private String author;
  private Contacts contacts;
  /* getter&setter + constructor */
}
 
public class Contacts {
  private String email;
  private String url;
  /* getter&setter + constructor */
}
 
XStream xstream = new XStream();
XStream xstream = new XStream(new DomDriver());
 
/* GLi alias rendono la serializzazione pi&ugrave; chiara */
xstream.alias("person", Blog.class);
xstream.alias("phonenumber", Contacts.class);
 
Blog blog = new Blog("DevMe", "mulp");
blog.setContacts(new Contacts("mulp@devme.it", "http://www.devme.it"));
String xml = xstream.toXML(blog);
<blog>
    <name>DevMe</name>
    <author>mulp</author>
    <contacts>
    	<email>mulp@devme.it</email>
    	<url>http://www.devme.it</url>
    </contacts>
</blog>

Al contrario, dando in input l’XML prodtto, si ottiene l’oggetto inizializzato:

1
Blog blog = (Blog)xstream.fromXML(xml);

L’esempio di prima dimostra la facilità e la flessibilità d’uso della libreria XStream. Unico particolare degno di nota è il fatto di poter definire degli alias per le classi che permette poi, in fase di serializzazione di poter generare tag coincisi dal nome uguale al nome della variabile membro, a meno che non si chieda di cambiarlo. Per intenderci, se non avessi definito gli alias otterei come tag il nome completo della classe per il tag blog assieme al nome del package, nell’esempio quello di default.

XStream usa il meccanimo delle Annotations per definire gli alias, rendendo così la definizione decisamente più pratica. Vediamo un esempio di codice che non definisce alias:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package it.devme.music;
 
public class Police {
    private String songTitle;
    public Police(String songTitle) {
        this.songTitle = songTitle;
    }
}
 
package it.devme.music;
public class JukeBox {
    public static void main(String[] args) {
        XStream stream = new XStream();
	Police song = new Police("Message in a bottle");
	System.out.println(stream.toXML(msg));
    }
}

L’esecuzione del codice produce il seguente XML:

1
2
3
    <it.devme.music.Police>
        <songTitle>Message in a bottle</songTitle>
    </it.devme.music.Police>

Come si può notare, senza l’utilizzo degli alias la serializzazione in XML non è coincisa, in particolare il nome della classe Persona eredita l’intero percorso del suo package di appartenenza. Definiamo ora gli alias usando le Annotations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package it.devme.music;
 
@XStreamAlias("police-songography")
public class Police {
    @XStreamAlias("song")
    private String songTitle;
    public Police(String songTitle) {
        this.songTitle = songTitle;
    }
}
 
package it.devme.music;
public class JukeBox {
    public static void main(String[] args) {
        XStream stream = new XStream();
	Police song = new Police("Message in a bottle");
	System.out.println(stream.toXML(msg));
    }
}

L’esecuzione del codice produce il seguente XML:

1
2
3
    <police-songography>
        <song>Message in a bottle</song>
    </police-songography>

Notate ora come il file XML è più chiaro e come il nome dei tag non dipende strettamente dai nomi delle classi e/o delle variabili membro, ma dagli alias definiti all’interno della classe stessa.

Come ultima cosa volevo soffermarmi su un’altra caratteristica di questa libreria, i Converter. Questo meccanismo da la possibilità di poter gestire in fase di serializzazione/deserializzazione l’esatta elaborazione che deve essere svolta in modo tale da avere una corretta esecuzione delle procedure. Per poter scrivere il proprio Converter è necessario implementare l’interfaccia Converter ed implementare i metodi proposti:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package it.devme.music;
public class PoliceConverter implements Converter {
    public boolean canConvert(Class clazz) {
        return false;
    }
 
    public void marshal(Object value, HierarchicalStreamWriter writer,
                        MarshallingContext context) {
        }
 
    public Object unmarshal(HierarchicalStreamReader reader,
                        UnmarshallingContext context) {
        return null;
    }
}

Il metodo canConvert verifica l’applicabilità del converter rispetto alla classe che si desidera pre/post elaborare. Il metodo marshal è una pre elaborazione, mentre unmurshal è una post elaborazione. I casi d’uso di un Converter variano dai più semplici, in cui si applica una trasformazione al tipo di dato che dovrà essere serializzato (si pensi ad esempio ad una data in formato millisecondi da trasformare in gg/mm/aaaa), ai più complessi in cui è necessario elaborare un nodo complesso contenente sottoalberi, ovviamente in entrambe le operazioni di serializzazione/deserializzazione. Vi rimando al tutorial sui Converter per gli esempi del caso.

See U on next post, enjoy !

Singleton pattern: quando e come usarlo.

Traendo ispirazione su un post trovato per la rete, ho deciso di scrivere un piccolo, ma proprio piccolo post, sul pattern Singleton, molto usato durante le sessioni di programmazione per risolvere una certa classe di problemi comuni. Definiamo un singleton come:
una classe per cui esiste una sola istanza nell’applicazione, e per la quale viene fornito un’unico punto globale di accesso.
Vediamo un possibile modo di implementare il singleton:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DevMeSingleton {
    /* class instance */
    private static DevMeSingleton instance;
    /* Costruttore privato della classe */
    private DevMeSingleton() {}
 
    /* Metodo che permette di ottenere l'istanza della classe */
    public static DevMeSingleton getInstance() {
        if (instance==null) {
            instance = new DevMeSingleton();
        }
        return instance;
    }
}

1. Semplice implementazione singleton pattern

L’implementazione di cui sopra, può causare problemi di performance nel contesto di un applicazione multithreading. E’ possibili che 2 thread ottengano in contemporanea 2 istanze della classe singleton, violando così un presupposto fondamentale della definizione. Per far sì che il comportamento non sia critico in un contesto del genere, si consideri l’utilizzo della tecnica: double-checked locking

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class DevMeSingleton {
        /* class instance */
        private volatile static DevMeSingleton instance;
 
        /* Costruttore privato della classe */
        private DevMeSingleton() {}
 
        /* Metodo che permette di ottenere l'istanza della classe */
        public static DevMeSingleton getInstance() {
            if (instance==null) {
                synchronized(DevMeSingleton.class) {
                    if (instance==null) {
                        instance = new DevMeSingleton();
                    }
                }
            }
        return instance;
    }
}

2. Tecnica double-checked locking

Questa tecnica purtroppo non funziona con la JVM 1.4 a causa della defizione della variabile volatile, la quale non era stata ancora implementata. Per ovviare a questo problema, vediamo l’implementazione successiva. 

1
2
3
4
5
6
7
8
9
10
11
12
13
public class DevMeSingleton {
 
        /* class instance */
        private static DevMeSingleton instance = new DevMeSingleton();
 
        /* Costruttore privato della classe */
        private DevMeSingleton() {}
 
        /* Metodo che permette di ottenere l'istanza della classe */
        public static DevMeSingleton getInstance() {
            return instance;
        }
}

3. Creazione efficiente dell’istanza di una classe Singleton

L’implementazione di cui sopra, garantisce la creazione efficiente dell’istanza della classe singleton, piuttosto di una creazione cosiddetta lazy (pigra). Il singleton viene creato all’atto dell’invocazione della classe, in quel momento vengono istanziate tutte le varibili static, tra cui anche l’istanza del singleton. Spero che possa essere utile. A presto.

Axis howTo

wsSalve, ultimamente ho avuto a che fare con lo sviluppo di web services, tecnologia molto interessante che permette di far dialogare sistemi sviluppati con diverse tecnologie…si pensi ad esempio, ad un back-end sviluppato in python e il front-end sviluppato in Java che effettua interrogazioni. Attraverso l’uso dei web services, è possibile realizzare una tale l’interazione. Sostanzialmente un web service, è un servizio lato server, che può essere invocato passando all’occorrenza dei parametri, e che può restituire un risultato. In Java, un modo abbastanza diffuso, è quello di utilizzare Axis, una libreria che permette lo sviluppo di web service, molto potente.Possono essere sviluppati utilizzando due modalità: contract-first, contract-last. La prima non prevede alcuna definizione del servizio, basta cioè sviluppare la classe che implementa il servizio ed esporla verso l’esterno. La seconda modalità, contract-last, prevede invece la scrittura di un file xml, chiamato web-service deployment descriptor (WSDD), che elenca al suo interno quali metodi della classe saranno esposti all’esterno, eventuale autenticazione per l’utilizzo del servizio ed eventuale mapping di tipi complessi. Una semplice richiesta per utilizzare un servizio consiste nella scrittura di un client che, invoca opportunamente un web service attraverso l’utilizzo delle librerie di Axis. Il compito delle librerie è quello di tradurre la richiesta in stream xml, il quale viene inviato al server utilizzando il protocollo HTTP come protocollo di trasporto. Lato server, lo stream xml viene ricevuto, tradotto e quindi eseguito il metodo. Dall’altra parte, l’eventuale risultato viene tradotto anch’esso in flusso XML e quindi inviato al client, che si occuperà di tradurlo. Vediamo di seguito come configurare Axis per fare in modo di eseguire in semplice web service, sia lato client che lato server. Bisogna innanzitutto scaricare axis da qui. Dopo aver decompresso il pacchetto, è possibile testare le applicazioni di default che sono contenuto all’interno, copiando il contenuto della directory webapps all’interno della directory webapps del vostro servlet-container preferito, nell’esempio consideriamo che sia tomcat, versione 5.5.25. Quindi collegandoci all’indirizzo http://localhost:8080/axis, potrete vedere in cosa consiste l’applicazione, che mostra anche un esempio di file WSDD. Per poter testare un web service invocandolo, è necessario perfezionare qualche dettaglio dell’installazione. Definiamo alcune variabili d’ambiente che memorizzano i path di installazione delle librerie di axis:

1
2
3
4
5
6
set AXIS_HOME=/opt/axis
set AXIS_LIB=$AXIS_HOME/lib
set AXISCLASSPATH=$AXIS_LIB/axis.jar:$AXIS_LIB/commons-discovery.jar:
$AXIS_LIB/commons-logging.jar:$AXIS_LIB/jaxrpc.jar:$AXIS_LIB/saaj.jar:
$AXIS_LIB/log4j-1.2.8.jar:$AXIS_LIB/xml-apis.jar:$AXIS_LIB/xercesImpl.jar
export AXIS_HOME; export AXIS_LIB; export AXISCLASSPATH

E’ chiaro dall’esempio che il sistema operativo di riferimento per i test è linux. Quindi definiamo le variabili d’ambiente fissandole sui valori dei path di axis, considerando di averle installate in /opt/axis. Fatto ciò, selezioniamo un esempio, dalla directory samples di axis, axis/samples/stock e prendiamo il descrittore del servizio per effettuare il deploy. Per effettuare il deploy sarà necessario invocare il web service Administrator integrato in Axis che realizza il deploy in Axis-engine del descrittore selezionato. Supponendo di posizionarci nella directory dove riesiede il file deploy.wsdd, l’invocazione per effettuare il deploy è la seguente:

1
2
java -cp $AXISCLASSPATH org.apache.axis.client.AdminClient
-lhttp://localhost:8080/axis/services/AdminService deploy.wsdd

se ottenete un errore del tipo ClassNotFoundException, probabilmente avete dei problemi con la definizione delle varibili d’ambiente per axis, quindi verificare che sia corretta. Se siete stati spinti dalla curiosità, probabilmente avrete dato un occhiata al contenuto del file wsdd, e quindi avrete intuito che viene richiesta l’autenticazione per accedere al servizio. Volendo testare a questo punto il web service, quello che dobbiamo fare è:

1
2
java -cp $AXISCLASSPATH samples.stock.GetQuote
-lhttp://localhost:8080/axis/servlet/AxisServlet -uuser1 -wpass1 XXX

che invoca il servizio chiamato GetQuote passando come parametro XXX in aggiunta a user e password. Il risultato che otteniamo, se tutto procede correttamente, è: "55.25". Quindi abbiamo visto come fare il deploy di un web service all’interno dell’engine di axis e come invocarlo da linea di comando. Vediamo ora, più o meno sinteticamente, come creare rispettivamente il lato server e quello client di un web service. Il nostro ambiente di sviluppo sarà eclipse J2EE, all’interno del quale cominceremo con lo sviluppare una "Dynamic web application" chiamata devmeWS. La nostra applicazione sarà veramente semplice ed elencherà i post presenti sul sito. Definiamo la struttura del nostro value object, ovvero del bean che memorizzerà le informazioni sui post, molto semplicemente si ha:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class PostVO implements Serializable {
    private String titolo;
    private String descrizione;
    public PostVO() {}
 
    public int getTitolo() {
        return titolo;
    }
 
    public void setTitolo(String titolo) {
        this.titolo = titolo;
    }
 
    public int getDescrizione() {
        return descrizionr;
    }
 
    public void setDescrizione(String descrizione) {
        this.descrizione = descrizione;
    }
}

Il nostro VO ha 2 campi che sono il titolo e la descrizione del post, con i relativi getter e setter. La logica banale del nostro web service è la seguente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CatalogoWS {
 
    public CatalogoWS() { }
 
    public Vector getListaPost() {
        Vector lista = new Vector();
        // post1
        PostVO post1 = new PostVO();
        post1.setTitolo(&quot;Spring Web Flow, DWR: perfect combination !&quot;);
        post1.setDescrizione(&quot;L'integrazione delle tecnologie pi&ugrave; diffuse....&quot;);
        // post2
        PostVO post2 = new PostVO();
        post2.setTitolo(&quot;Il grosso grasso JAR....&quot;);
        post2.setDescrizione(&quot;Come si esegue un ricco jar&quot;);
        return lista;
     }
// web service descriptor deployment
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
            xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
    <service name="urn:catalogoWS" provider="java:RPC" style="rpc" use="  encoded">
        <parameter name="className" value="it.devme.ws.CatalogoWS"/>
        <parameter name="allowedMethods" value="getListaPost"/>
        <parameter name="scope" value="Session"/>
        <beanMapping qname="myNS:PostVO" xmlns:myNS="urn:catalogoWS"  languageSpecificType="java:it.devme.vo.ProdottoVO"/>
    </service>
</deployment>

Sostanzialmente il web service, costruisce una lista di oggetto Post e la restituisce al client che ne visualizzerà il contenuto. Dobbiamo costruire a mano il nostro wsdd, anche questo molto banale che definisce il nostro web service, il riferimento alla classe che lo implementa ed il/i metodi che sono esposti verso l’esterno. Osserviamo che abbiamo definito anche un nostro tipo di dato custom, PostVO mappato su un namespace di esempio chiamato myNS. La definizione di tale tipo permette di poter serializzare/deserializzare il risultato fornito dal server o la richiesta inviata dal client, in xml, in modo tale da poter essere letto o scritto all’occorrenza. Più semplicemente, quando un web service invia un risultato, se questi è di un qualche tipo primitivo, viene serializzato in un tag xml che lo contiene. Se il risultato è di tipo più complesso, ad esempio PostVO, dovrà anch’esso essere serializzato in una qualche forma tale per cui sarà poi possibile la de-serializzazione client-side. Queste 2 operazioni, serializzazione/deserializzazione, sono possibili grazie al mapping nel file descrittore dei tipi predefiniti. Il client si basa anch’esso sulle librerie di axis, che permettono di invocare il metodo remoto e manipolare il risultato.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Client {
 
    public static void main(String[] args) {
        try {
            Call call = (Call) new Service().createCall();
            call.setTargetEndpointAddress(new URL("http://localhost:8080/productWS/services"));
            QName qnameProd = new QName("urn:catalogoWS", "PostVO");
            Class classeProd = PostVO.class;
            call.registerTypeMapping(classeProd, qnameProd, BeanSerializerFactory.class,
            //richiamo il metodo getListaPost
            call.setOperationName(new QName("urn:catalogoWS", "getListaPost"));
            Object rispostaWS = call.invoke(new Object[]{});
            Vector lista = (Vector) rispostaWS;
            System.out.println("Il catalogo comprende:");
            Iterator iter = lista.iterator();
            PostVO item = null;
            while (iter.hasNext()) {
                item = (PostVO) iter.next();
                visualizza(item);
            }
        } catch (Exception ex) {
            System.out.println("Si &egrave; verificata l'eccezione: " + ex.toString());
        }
    }
}

Il client è un’applicazione classe Java semplice che usa le librerie di axis per effettuare l’invocazione del metodo remoto. Viene fissato il tipo di dato custom e mappato sulla relativa classe che lo implementa, che ovviamente, è necessaria anche sul lato client. Viene impostato il nome del metodo da invocare, come nome dell’operazione da svolgere ed infine viene effettuata l’invocazione, la quale se non ci sono errori, restituisce un vettore di oggetti Post che vengono visualizzati all’interno del ciclo seguente la chiamata. Da osservare una cosa molto importante, e cioè che affinché tutto funzioni, sarà necessario installare il servizio creato, all’interno di axis engine che provvederà quindi a renderlo disponibile.

1
2
java -cp $AXISCLASSPATH org.apache.axis.client.AdminClient
-lhttp://localhost:8080/axis/services/AdminService deploy.wsdd

Spero di essermi ricordato tutto correttamente e senza errori, era da un pò di tempo che volevo pubblicare un post del genere, ma solo oggi sono riuscito a ritagliare in tempo per farlo. Sono andato a mente spero di non aver ricordato male. Qui trovate l’esempio di cui sopra, client e server implementati. Buone vacanze.

Spring Web Flow, DWR: perfect combination !

springDopo un lungo letargo arieccomi qui a parlare di questioni informatiche che trovo prima di tutto molto divertenti (vi chiederete: che tipo è questo?) e dopo molto interessanti. Trovandomi ultimamente occupato allo sviluppo di un applicazione web di cui spero sentirete parlare molto presto, ho provato dapprima e poi ormai adottato la tecnologia messa a disposizione da Spring Web Flow, un framework basato su Spring che permette di definire dei flussi di navigazione per un applicazione web in modo semplice, chiaro e potente…..molto potente. Tralasciamo qui la discussione dei dettagli della tecnologia, per descrivere quello che in sostanza è un integrazione tra SWF, DWR Direct Web Remoting, un framework che aggiunge il supporto AJAX alle nostre applicazioni. Come dicevo, non starò a dettagliare ciò che vogliono dire i file di configurazione, il significato, etc etc, dando tutto per scontato….in futuro se risciro magari, pubblicherò qualche tutorial su Spring framework e spring web flow, anche se in rete si trova di tutto. Ciò detto….let’s start! Occurre naturalmente scaricare la libreria: DWR. Dopo di che si procede con ordine. Sinteticamente, ricordiamo che DWR permette di richiamare attraverso una call-back, dei metodi remoti esposti server side. I risultati restituiti dalla chiamata ai metodi, che possono essere di tipo semplice (int, String, boolean) o complesso come Array di oggetti o di tipi semplice. I risultati vengono "trasformati" e forniti come varibili javascript (array, stringhe o quant’altro) le quali possono essere usati poi localmente. Editiamo il file web.xml aggiungenfo le seguenti linee:

1
2
3
4
5
6
7
8
9
10
11
12
<servlet>
    <servlet-name>dwr</servlet-name>
    <servlet-class>org.directwebremoting.spring.DwrSpringServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>true</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>dwr</servlet-name>
    <url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

le quali inizializzano la servlet che permetterà il riconoscimento delle chiamate verso gli script javascript per realizzare l’interazione AJAX. L’integrazione di DWR 2.0 con Spring 2.5 è molto più light rispetto alla versione precedente. Sostanzialmente basta aggiungere il namespace di dwr in testa al file di configurazione globale di spring, definire il controller e qualche mapping di URL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
          http://www.directwebremoting.org/schema/spring-dwr
         http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd">
 
    <dwr:controller id="dwrController" debug="true" />
    <bean id="urlMapping" 
             class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <!-- DWR configuration -->
                <prop key="/dwr/**/*.*">dwrController</prop>
                <prop key="/dwr/**/*">dwrController</prop>
                <prop key="/dwr">dwrController</prop>
                <prop key="*.html">dwrController</prop>
            </props>
        </property>
        <property name="alwaysUseFullPath" value="true"/>
    </bean>
</beans>

Successivamente ci rimane da dichiarare il metodo remoto che si vuole esporre e la eventuale classe che si desidera gestire dal client con javascript. Il tutto si fa dal file xml, che per chiarezza scrivo sempre a parte e poi lo includo in web.xml come facente parte della configurazione.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
File: dwr.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
          http://www.directwebremoting.org/schema/spring-dwr
          http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd">
    <bean id="dwrexposure" class="it.devme.dwr.exposure.DWRExposure">
        <dwr:remote javascript="DWR">
            <dwr:include method="retriveProductCompanyList" />
        </dwr:remote>
        <property name="baseManager"><ref bean="baseManager"/></property>
    </bean>
    <dwr:configuration>
        <dwr:convert type="bean" class="it.devme.dwr.exposure.ProductsVO" />
    </dwr:configuration>
</beans>

Praticamente abbiamo fatto, DWRExposureè la classe che contiene il metodo retriveProductCompanyList che intendiamo esporre, che restituisce un array di oggetti ProductsVO i quali vengono convertiti e resi utilizzabili da locale da DWR, con il tag dwr:convert che indichiamo. L’oggetto ProductsVO è un semplicissimo Java Bean contenente 2 campi che sono l’id del prodotto e la sua descrizione. Da client side, includiamo i seguenti script:

1
2
3
<script type="text/javascript" src="<c:url value="dwr/interface/DWR.js"/>"></script>
<script type="text/javascript" src="<c:url value="dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="dwr/util.js"/>"></script>

Il primo viene creato da DWR e contiene la logica che permette l’invocazione del metodo remoto e il recupero del risultato. Le altre 2 inclusioni permettono l’utilizzo di alcune funzioni di utilità molto comode. Vediamo ora come fare a usare il tutto. Use case: 2 select, uno che contiene una lista di utenti. L’altro conterrà la lista dei prodotti associati all’utente. La selezione del primo, fa scattare l’invocazione del metodo remoto che riempie il secondo select con i dati dei prodotti associati all’utente selezionato:

1
2
3
4
5
6
7
8
<select name="user_id" id="user_id" onchange="loadProduct();">
    <option value="0">Scegli</option>
    <option value="1">Utente1</option>
    <option value="2">Utente2</option>
</select>
<select name="product_id" id="product_id" >
    <option value="0">Scegli</option>
</select>

Quindi selezionando l’utente dalla lista, verrà richiamato il codice javascript che andremo a scrivere, che richiamerà il metodo remoto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/** Call Remote method and
load products data */
function loadProduct() {
    var user_id = DWRUtil.getValue("user_id");
    if (user_id>0) {
        DWR.retriveProductsList(user_id, gotProducts);
    } else {
        dwr.util.removeAllOptions("product_id");
        dwr.util.addOptions("product_id", [ { name:'Scegli..', id:'0' } ], "id", "name");
        document.campaign.product_id.disabled=true;
    }
}
function gotProducts(products) {
    dwr.util.addOptions("product_id", products, "idproduct", "name");
    document.campaign.product_id.disabled=false;
}

La prima funzione loadProduct() richiamerà il metodo remoto, selezionando l’utente dall’elemento select attraverso la funzione di utilità DWRUtil.getValue("user_id"). Il metodo richiamato restituirà il risultato alla funziona gotProducts(products) fornendolo come parametro. L’array di oggetti a questo punto, conterrà le informazioni sui prodotti associati all’utente, e può essere assegnato al select dei prodotti attraverso l’altra funzione di utilità dwr.util.addOptions("product_id", products, "idproduct", "name"). E il gioco è fatto…con una semplicità incredibile, senza requirements mostruoso o complessi si aggiunge il supporto ad AJAX alla nostra applicazione Java, in particolare Spring. Con queste informazioni in mani ci si può sbizzarire creando il proprio personale componente AJAX. Nel prossimo articolo vedremo come integrare il supporto per JCaptcha per rendere più sicure le nostre applicazione. Alla prossima.

Array & Java Reflection

In questo periodo si lavora abbastanza…si programma come al solito usando Java…il mio linguaggio di programmazione di riferimento. Ho avuto a che fare con la riflessione, approcci comuni quali richiamare dei metodi su oggetti, prelevarne i valori delle variabili membro, etc…il tutto ovviamente fatto a run-time. Ho letto un piccolo hint su come recuperare l’istanza di una classe di un Array usando la riflessione in Java. Ricordiamo come si recupera l’istanza di una classe usando la riflessione:

  • Class.forName("it.devme.MYClass"), per ottenere l’istanza dal nome della classe
  • MYClass.class, per ottenere l’istanza dal tipo
  • Integer.TYPE, per ottenre l’istanza da qualunque tipo primitivo

E nel caso di un array ??
Come suggerito nell’articolo, un array è anch’esso un oggetto e quindi possiede una classe, ma con i metodi suggeriti sopra non è possibile ottenerne l’istanza a runtime. Un modo per fare ciò è il seguente:

  • Class.forName("[C"), per un array di caratteri. Si specifica come nome [C che corrisponde al nome della rappresentazione data dal linguaggio agli array di caratteri. Se vi capita di fare debug con un IDE a caso, ad esempio eclipse, vi accorgerete che la rappresentazione di un oggetto array di caratteri data dal linguaggio Java inizia con [C
  • Class.forName("[Ljava.lang.String;"), per un array di stringhe.

L'articolo si conclude con un suggerimento dato da un guru di Java, Tim Eck, che suggerisce di usare semplicemente l'istruzione:

char[].class

per ottenere l’istanza di una classe di array di caratteri.

Il grosso grasso JAR….

jarSalve…non sono morto, è che sto lavorando abbastanza e quindi trovare il tempo per parlare di cose interessanti, anche se mi piace molto, è difficile !!! ma poi con chi sto parlando visto che siete sempre così pochi ?? [crisi esistenziale]. Ciò detto, volevo rendervi partecipe di questa mia piccola scoperta…che poi scoperta in realtà non è, ma dal momento che a lavorare ci si imbatte nei problemi, questa volta la soluzione volevo condividerla…sai mai che a qualcuno possa tornare utile. Dunque il problema è il seguente: creare un jar contenente al suo interno altri jar, ovvero le librerie, che possa funzionare senza dover specificare nulla nel classpath.
Sostanzialmente, dato il jar devme.jar voglio lanciarlo usando il comando:

1
 java -jar devme.jar

senza preoccuparmi di altro.
Cercando su gooogle ho trovato un articolo della IBM che mi ha illuminato sul fare alcuni esperimenti, e quindi sul risolvere il problema…vediamo assieme.
Il classloader di java, per gli amici sun.misc.Launcher$AppClassLoader, che viene richiamato al lancio del comando java -jar, è a conocenza di 2 cose: 

  • Carica classi/risorse che compaiono nella root del JAR.
  • Carica classi/risorse che compaione nell’attributo Class-Path del file MANIFEST.MF.

Inoltre, ignora qualunque valore della variabile d’ambiente CLASSPATH o argomento fornito da riga di comando -cp, usato per specificare il classpath. Dulcis in fundo, si fa per dire, non sa come caricare classi/risorse all’interno di JAR presenti all’interno del jar da eseguire. Per cominciare creaimo un singolo JAR, che sarà il nostro eseguibile e che quindi chiamiamo main.jar. Supponiamo di avere una classe entry-point it.devme.main.Main e assumiamo che dipenda da 2 classi: it.devme.a.A all’interno del jar a.jar e it.devme.b.B all’interno del jar b.jar.

main.jar | it/devme/main/Main.class | it/devme/a/A.class | it/devme/b/B.class

Questo approccio ha delle limitazioni tali da suggerire l’utilizzo di un altro metodo. una di queste è che l’informazioni sulla provenienza originale della classi A.class e B.class viene persa. Un altra più importante è la seguente:
se a.jar e b.jar contengono una risorsa con lo stesso nome, quale scelgo? Cambiamo strada. Un altro approccio è quello di modificare il MANIFEST.MF a manoni, cercando di comporre quello di main.jar in modo che avesse visibiltà degli altri jar. Ma l’unica cosa che si riesce a fare è quella di porli nel filesystem a fianco di main.jar che è esattamente la cosa che si voleva evitare.

Per tagliare la testa al toro, il suggerimento dato è quello di scrivere un class loader personalizzato, in modo da caricare le classi che servono dall’interno di un JAR. Si tenga presente che scrivere un class loader personalizzato non è un operazione da prendere alla leggera, dal momento che questa ha un impatto molto profondo con il resto dell’applicazione, dal momento che si preoccupa di caricare le classi e di rispondere agli errori quando questi si verificano. Il concetto di class loader va oltre lo scopo di questo post, per cui ulteriori dettagli non verranno trattati. Tenendo presente la struttura del nostro jar:

one-jar.jar | META-INF/MANIFEST.MF | main/main.jar | lib/a.jar | lib/b.jar

proviamo a scrivere il nostro class-loader. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
com/simontuffs/onejar/JarClassLoader.java
protected URL findResource(String $resource) {
    try {
        // resolve($resource) returns the name of a
        // resource in the
        // byteCode Map if it is known to this
        // classloader.
        String resource = resolve($resource);
        if (resource != null) {
            // We know how to handle it.
            return new URL(Handler.PROTOCOL + ":" + resource);
        }
        return null;
    } catch (MalformedURLException mux) {
        WARNING("unable to locate " + $resource);
    }
    return null;
}

Si noti subito che per il recupero di una classe si utilizza un URL con rispettivo protocollo che permette di identificare una risorsa. Il protocollo in questo caso è un protocollo custom, che comincia con il prefisso onejar:. Di seguito abbiamo l’handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
com/simontuffs/onejar/Handler.java
package com.simontuffs.onejar;
...
public class Handler extends URLStreamHandler {
/**
* This protocol name must match the
* name of the package in which this class
* lives.
*/
   public static String PROTOCOL = "onejar";
   protected int len = PROTOCOL.length()+1;
   protected URLConnection openConnection(URL u) throws IOException {
       final String resource = u.toString().substring(len);
       return new URLConnection(u) {
 
           public void connect() {}
 
           public InputStream getInputStream() {
               // Use the Boot classloader to get the resource.
               // is only one per one-jar.
               JarClassLoader cl = Boot.getClassLoader();
               return cl.getByteStream(resource);
           }
       };
   }
}

Il nostro class loader andrà inserito nel MANIFEST.MF/Main-Class attribute. Verrà creato un nuovo bootstrap della main class, com.simontuffs.onejar.Boot, la quale è specificata come Main-Class attribute. La nuova classe creerà una nuova istanza del JarClassLoader userà il nuovo loader, per caricare it.devme.main.Main.class usando la riflessione per invocare il metodo main(). Finito di leggere l’articolo IBM che è possibile trovare qui, o forse nel mentre della lettura non ricordo, ho notato l’indirizzo di questo meraviglioso, risolvi problemi, plugin per eclipse….che realizza né più né meno la tecnica sopra descritta per produrre un jar eseguibile con l’accesso a librerie al suo interno. Il plugin lo trovate qui…è ancora una pre-release alpha, ma fino ad ora non ho avuto alcun problema nell’utilizzo.

 

You need to log in to vote

The blog owner requires users to be logged in to be able to vote for this post.

Alternatively, if you do not have an account yet you can create one here.

Powered by Vote It Up

WordPress Themes