Posts tagged: xml

SWF: Model Data Validation

spring-webflowDopo aver visto come SWF realizza il mapping tra i dati di un modello e i campi di un form, vediamo ora come creare il meccanismo di validazione che consente di verificare il valore di ciascun campo inserito all’interno di un form. In questo articolo abbiamo visto come viene realizzata il binding dei dati e teniamolo come punto di partenza per iniziare a vedere il meccanismo della validazione.
Riprendiamo il codice sorgente del file di configurazione del flow e la pagina HTML all’interno della quale è presente il form:

<?xml version="1.0" encoding="UTF-8"?>
<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">
 
    <var name="flight" class="it.devme.flight.Flight" />
 
    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" />
    </view-state>
 
    <view-state id="confirm" view="flight/showFlightInfo" />
 
</flow>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>DevMe - SWF Flight info test - </title>
  </head>
 
<body>
  <h1>Flight information </h1>
  <p>Inserisci le informazioni sul volo:</p>
    <form:form id="flightDetails" modelAttribute="flight">
      <table>
        <tr>
          <td>Numero del volo: </td>
         <td><form:input path="number"/></td>
        </tr>
 
        <tr>
          <td>Ora di partenza: </td>
          <td><form:input path="time"/></td>
        </tr>
 
        <tr>
         <td>Gate: </td>
         <td><form:input path="gate"/></td>
        </tr>
 
        <tr>
          <td>Volo in ritardo: </td>
          <td><form:radiobutton path="isDelayed" value="false" />
  	      <form:radiobutton path="isDelayed" value="true" /></td>
        </tr>
 
        <tr>
          <td><input type="submit" name="_eventId" value="next"></td>
        </tr>
    </table>
    </form:form>
  </body>
</html>

Bene. Come già ampiamente discusso, il flow definito dal file XML di sopra è molto semplice. Abbiamo una sola transizione da uno stato all’altro, ovvero si naviga da una pagina ad un altra pagina. Supponiamo ora, di voler validare i dati del form della pagina di partenza, e spostarsi nella pagina di arrivo, solo se non esistono errori di validazione secondo il nostro personale schema di validazione. Il nostro schema, prevede per semplicità che almeno uno dei tre campi presenti nel form sia indicato, e che se indicato il numero del volo questi dovrà essere un valore numero e eventuali caratteri alfabetici verranno rigettati.

Iniziamo con la modifica del file XML di definizione del flow:

<?xml version="1.0" encoding="UTF-8"?>
<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">
 
    <var name="flight" class="it.devme.flight.Flight" />
 
    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" >
          <action method="bindAndValidate" bean="flightAction" />
      </transition>
    </view-state>
 
    <view-state id="confirm" view="flight/showFlightInfo" />
 
</flow>

Il metodo bindAndValidate in sostanza richiama la nostra classe validator, la quale effettua i controlli che andremo a definire. Da notare che il riferimento nel tag action, punta ad un metodo all’interno della nostra classe Action che in realtà non esiste, ma che viene ereditato dalla superclasse FormAction. Definendo all’interno del file XML di definizione del bean il riferimento alla nostra classe Validatore, autormaticamente verrà richiamata all’invocazione del metodo bindAndValidate. Il codice della classe che si occupa di effettuare la validazione è il seguente: 

package it.devme.flight;
 
public class FlightValidator implements Validator {
 
	public boolean supports(Class clazz) {
		return clazz.equals(Flight.class);
	}
 
	public void validate(Object target, Errors errors) {
                Flight flight = (Flight) target;
                if (flight.getNumber()==0 && flight.getTime()==null && flight.getGate.equals("")) {
                    errors.rejectValue("gate", "Attenzione, &egrave; necessario specificare almeno 1 tra: numero del volo, ora di partenza, Gate");
                }
 
                if (errors.getErrorCount()==0) {
                    ValidationUtils.rejectIfEmptyOrWhitespace(errors, "gate", "Il campo gate non pu&ograve; essere vuoto.");
                }
	}
 
}

Vediamo cosa è stato fatto. Intanto osserviamo che FlightValidator implementa la classe Validator, quindi definisce alcuni metodi essenziali che sono supports e validate. Il primo assicura il supporto al Bean indicato al suo interno. Il secondo implmenta la logica di validazione. Nel nostro caso vogliamo che almeno uno dei tre campi venga inserito, e che il campo Gate sia diverso da vuoto. Il primo controllo verifica la prima delle nostre condizioni; il secondo if, verifica prima che non ci siano stati errori in precedenza, e in caso positivo effettua il controllo di validazione sul campo Gate.

Vediamo ora come modifichiamo il file HTML per presentare il messaggio di errore:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>DevMe - SWF Flight info test - </title>
  </head>
 
<body>
  <h1>Flight information </h1>
  <p>Inserisci le informazioni sul volo:</p>
    <form:form id="flightDetails" modelAttribute="flight">
      <table>
        <tr>
          <td>Numero del volo: </td>
         <td><form:input path="number"/></td>
        </tr>
 
        <tr>
          <td>Ora di partenza: </td>
          <td><form:input path="time"/></td>
        </tr>
 
        <tr>
         <td>Gate: </td>
         <td><form:input path="gate"/></td>
         <td><form:errors path="gate"/></td>
        </tr>
 
        <tr>
          <td>Volo in ritardo: </td>
          <td><form:radiobutton path="isDelayed" value="false" />
  	      <form:radiobutton path="isDelayed" value="true" /></td>
        </tr>
 
        <tr>
          <td><input type="submit" name="_eventId" value="next"></td>
        </tr>
    </table>
    </form:form>
  </body>
</html>

Per semplicità il nostro messaggio di errore comparirà sempre nello stesso posto, ovvero sotto il campo Gate. Così nel primo caso di errore, darà contezza del fatto che almeno uno dei campi di precedenti deve essere indicato, mentre nel secondo caso, sarà indicativo del campo stesso. Come vedete, l’aggiunta del tag <form:errors/> fa in modo di collegare eventuali codici di errori con il campo presentato in pagina.

Alla prossima. Stay tuned!

SWF: Model Data Binding

spring-webflowNell’ultimo articolo su Spring Web Flow ho condensato la maggior parte dei concetti chiave sull’argomento. Mi piacerebbe ora tornare indietro su alcune cose cercando di porre maggiore attenzione. In questo articolo vedremo un pò più da vicino come avviene il binding dei dati in SWF, da un form HTML verso un bean.
In SWF esiste un controller, dello stesso genere di quelli utilizzati in Spring MVC (es. AbstractFormController), che permette di effettuare il binding dei parametri corrispondenti ai campi di un form HTML, quindi presente nella request HTTP verso un command object rappresentato da una semplicissima classe Java Bean. Ad una certa view è associato un certo model object sul quale verranno memorizzati i valori dei parametri del form e affinché avvenga in binding è sufficiente dichiarare l’attributo model dell’elemento <view-state>. L’esempio che segue mostra come deve essere definito il tutto nel contesto SWF affinché avvenga correttamente il binding.

Si comincia col definire la classe del nostro Java Bean, suppioniamo di voler memorizzare le informazioni riguardanti i voli in partenza da un certo aeroporto.

package it.devme.flight;
 
import java.io.Serializable;
import java.sql.Time;
 
public class Flight implements Serializable {
 
        private static final long serialVersionUID = 1L;
 
        private String number;
        private Time time;
	private String gate;
	private boolean isDelayed;
 
	public String getNumber() {
		return number;
	}
	public void setNumber(String number) {
		this.number = number;
	}
	public Time getTime() {
		return time;
	}
	public void setTime(Time time) {
		this.time = time;
	}
	public String getGate() {
		return gate;
	}
	public void setGate(String gate) {
		this.gate = gate;
	}
	public boolean isDelayed() {
		return isDelayed;
	}
	public void setDelayed(boolean isDelayed) {
		this.isDelayed = isDelayed;
	}
 
}

Fin qui niente di più semplice. Procediamo ora con il nostro flow. Il flow consiste di 2 view. La prima pagina raccoglie i dati che verranno memorizzati all’interno dei 4 attributi della classe Flight. Avanzando con il bottone next, la seconda pagina semplicemente mostrerà i dati raccolti.

<?xml version="1.0" encoding="UTF-8"?>
<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">
 
    <var name="flight" class="it.devme.flight.Flight" />
 
    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" />
    </view-state>
 
    <view-state id="confirm" view="flight/showFlightInfo" />
 
</flow>

All’inizio del flow creiamo la variabile di flow flight la quale è un istanza della classe Flight. Fissando questa variabile come attributo model nella start view, diciamo a SWF di utilizzare questo oggetto per effettuare il binding dei dati. Implementando una semplicissima pagina HTML con un form chiudiamo il primo cerchio.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>DevMe - SWF Flight info test - </title>
  </head>
 
<body>
  <h1>Flight information </h1>
  <p>Inserisci le informazioni sul volo:</p>
    <form:form id="flightDetails" modelAttribute="flight">
      <table>
        <tr>
          <td>Numero del volo: </td>
         <td><form:input path="number"/></td>
        </tr>
 
        <tr>
          <td>Ora di partenza: </td>
          <td><form:input path="time"/></td>
        </tr>
 
        <tr>
         <td>Gate: </td>
         <td><form:input path="gate"/></td>
        </tr>
 
        <tr>
          <td>Volo in ritardo: </td>
          <td><form:radiobutton path="isDelayed" value="false" />
  	      <form:radiobutton path="isDelayed" value="true" /></td>
        </tr>
 
        <tr>
          <td><input type="submit" name="_eventId" value="next"></td>
        </tr>
    </table>
    </form:form>
  </body>
</html>

Cliccando sul Next ci spostiamo verso la seconda view definita all’interno del flow attraverso la transition confirm. All’interno di questa transition i valori dei campi del form vengono inviati in POST e automaticamente ne viene fatto il bind verso l’oggetto flight presente nel flow scope. Successivamente, durante il caricamento della view confirm, vengono eseguite le espressioni EL rispetto al valore dei campi del bean, consentendoci quindi di visualizzarne il valore.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>Flight Information Details</title>
  </head>
 
<body>
 
  <h1>Flight Information Details</h1>
  <p>Dettagli del volo:</p>
  <form:form modelAttribute="flight" >
 
    <table>
      <tr>
        <td>Numero volo: </td>
        <td>${flight.number}</td>
      </tr>
 
      <tr>
       <td>Orario: </td>
       <td>${flight.time}</td>
      </tr>
 
      <tr>
       <td>Gate: </td>
       <td>${flight.gate}</td>
      </tr>
 
      <tr>
       <td>Ritardo: </td>
       <td>${flight.isDelayed}</td>
      </tr>
 
   </table>
 </form:form>
</body>
</html>

Come vedete è molto semplice. Una piccola nota: se per qualunque ragione non si volesse proseguire durante una transition, al binding dei parametri della request è sufficiente aggiungere l’attributo bind all’interno del tag transition indicando come valore false.

    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" />
      <transition on="cancel" to="end" bind="false" />
    </view-state>

Nel prossimo articolo parleremo della validazione dei campi di un form. Alla prossima
Stay tuned!

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 !

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.

 

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