Salve di nuovo :)
In questo post volevo parlare di un argomento che mi sta facendo penare non poco ultimamente :P
Intendo parlare di BPEL (Business Process Execution Language).
E' un linguaggio basato su file XML e la struttura del file non serve solo a descrivere i processi di business ma anche per eseguirli, quindi può essere considerato come un linguaggio di programmazione.
Può essere visto come un linguaggio interpretato e gli interpreti sono i motori BPEL. Questi motori non funzionano come un'interprete per un classico linguaggio di programmazione come potrebbe essere l'interprete Perl, ma piuttosto funzionano come dei container per processi di business.
Infatti per essere eseguito, un processo BPEL deve essere "deployato" su un motore, che ne controlla la validità e, se conforme alla specifica, lo rende "eseguibile" dai consumer.
Uso il termine "consumer" perchè BPEL è pensato principalmente per essere utilizzato in architetture SOA (Service Oriented Architecture) ed in esse l'utilizzatore di un servizio è chiamato consumer, che in linea di massima può essere considerato il client.
Personalmente ho fatto alcune prove utilizzando il motore Apache ODE (Orchestration Director Engine), il motore messo a disposizione da Intalio BPMS (Business Process Management System) ed il motore installabile in Virtuoso Universal Server tramite un'estensione.
BPEL è utilizzato in architetture Service-Oriented perché fondamentalmente un processo è visto come un servizio, o meglio, una volta fatto il deploy di un processo su un motore questo viene esposto verso l'esterno proprio come un web service che espone le proprie informazioni tramite WSDL.
Il processo, quindi, è un servizio a tutti gli effetti. Ai client che effettuano richieste il fatto che "dietro" la loro richiesta si nasconde un motore BPEL che esegue processi di business è assolutamente trasparente, essi vedono un servizio e nient'altro.
Cosa ha di diverso un processo BPEL, quindi, rispetto ad un normale web service?
BPEL può essere visto come uno strumento per offrire un servizio che scaturisce dalla composizione di altri servizi. In un'architettura SOA si parla di orchestrazione di servizi. Un processo BPEL quindi non espone operazioni semplici. Esso non fa altro che richiamare altri servizi per raggiungere un determinato scopo. Ad esempio può richiamare in sequenza diversi servizi utilizzando l'output del precedente come input del successivo, richiamare ciclicamente dei servizi ed eseguire anche parallelamente richieste a servizi diversi.
Alla fine del suo lavoro può ovviamente restituire un risultato al consumer oppure un fault. Possono essere eseguite sia richieste sincrone che asincrone. Insomma, è un web service :)
E' una cosa utile? comoda?
Potenzialmente si, praticamente ho i miei dubbi..
Come ogni cosa, ci sono dei pro e dei contro.
Pro:
L'unico che personalmente abbia trovato è che essendo un linguaggio XML-based, la sua struttura è ben definita, è facile eseguirne il parsing e la validazione. Questo ha reso possibile lo sviluppo di tool molto comodi per lo sviluppo di processi. Editor grafici che permettono di fare il design con il classico drag-and-drop.
Personalmente ho provato ad utilizzare il plugin per Eclipse BPEL Designer ed Intalio BPMS. Quest'ultimo non è proprio un designer BPEL. E' un designer per processi di business, ma modellati tramite BMPN (Business Process Model and Notation) che, a differenza di BPEL, è stato progettato solo per modellare i processi di business. Intalio comunque dal modello BPMN genera un file BPEL che può essere installato in qualsiasi motore. Ne esistono diversi altri, sia opensource che con licenze commerciali, ma non li ho mai utilizzati.
Devo dire che soprattutto Intalio è veramente comodo per modellare i processi, anche BPEL Designer lo è, ma richiede una conoscenza maggiore di BPEL per essere utilizzato.
Contro:
Purtroppo i contro (sempre riguardo la mia esperienza) sono molti di più dei pro.
Innanzitutto, come succede in molti editor grafici che cercano di fare ogni cosa dall'interfaccia senza eseguire nessun intervento sul codice, si perde ogni controllo su cosa succede "sotto" il designer. Questo problema l'ho riscontrato soprattutto con Intalio in quanto il loro framework è stato progettato per avere quello che viene chiamato zero-code development.
Effettivamente lo scopo è raggiunto, infatti anche volendo non c'è la minima possibilità di modificare a mano il codice dei processi. L'unico modo sarebbe prendere il file BPEL generato e modificarlo con un altro editor.
Con BPEL Designer, invece, la cosa non è così grave. Nonostante tramite l'editor grafico si possa fare praticamente tutto, è possibile modificare direttamente il codice sorgente del processo. Il problema è che non sempre l'interfaccia grafica "si accorge" delle modifiche fatte a mano.
Un altro problema, che personalmente trovo molto grave, è che siccome c'è un motore che legge il file XML ed in base al suo contenuto esegue delle azioni, è impossibile eseguire il debug di un processo. I tool grafici possono dire se ci sono errori di validazione. A volte si riesce ad eseguire il deploy di processi che non passano la validazione, ma non sempre è così. Questo non è un grosso problema, perché ad esempio ODE al momento del deploy se c'è qualcosa che non va da degli errori di compilazione, indicando dov'è il problema. I messaggi di errore a volte sono chiari, altre volte sono di difficile comprensione, ma almeno da qualche errore subito. Il problema nasce quando ci sono problemi nella logica, come qualche conversione dei dati, qualche assegnamento errato (si, un semplice assegnamento può essere qualcosa di molto poco semplice in BPEL), una condizione di uscita di un ciclo che non si verifica ecc..
E' comprensibile che non sia possibile eseguire un vero e proprio debug di un processo, ma il problema è che non si riesce ad avere nessun controllo sul processo installato. Quindi niente log personalizzati, niente messaggi ecc. Anche restituire un fault in base ad una condizione non è una cosa così semplice, ma li è colpa mia che non mi sono visto bene l'argomento :)
Quindi, per il capire cosa c'è che non va in un processo ci si deve basare sui messaggi di fault e sui log del motore. Ancora una volta, non sempre questi sono immediatamente comprensibili. Nella mia (breve) esperienza ogni volta che c'era qualcosa che non andava dovevo fare diverse ricerche per capire il significato dei messaggi di errore.
Ancora un altro problema: BPEL mal si adatta ai cambiamenti.
Infatti su diversi post, non necessariamente scritti da detrattori di BPEL (addirittura su un blog di un tipo che dava dei consigli ai neofiti), ho letto che quando c'è da fare qualche cambiamento o c'è qualcosa che non va in un processo, la cosa migliore è rimodellarlo daccapo. O_O
Ok, è una cosa che posso anche accettare se sto modellando un processo di Hello World, ma che succede se devo modificare un processo che magari richiama 10 servizi in parallelo, per ognuno gestisce i fault ed in base ad i risultati ricevuti decide se e quali altri servizi chiamare?
Ancora. Gli errori di validazione.
Non so voi, ma in generale ogni volta che c'è un errore di validazione in un file XML è un mal di testa.
Quando ci sono errori di validazione in file BPEL, o WSDL è ancora peggio.
Si, a volte si devono modificare anche i file WSDL di altri servizi che magari già sono funzionanti e installati in un web service engine perché per esempio (e mi è capitato con ODE) non supporta una direttiva import per importare schemi XSD che non siano locali.
Aderenza agli standard.
Come ho detto alcuni motori fanno i capricci su alcune cose, ad esempio Apache ODE con la questione degli import nei file WSDL.
Sempre per quanto riguarda Apache ODE (si è capito che è quello che ho utilizzato maggiormente? ) ho dovuto sbattere non poco la testa cercando di usare SOAP 1.2 per poi scoprire che solo l'1.1 è supportata.
La versione 1.2 è raccomandazione W3C dal lontano giugno 2003... ed ODE è un progetto tutt'altro che morto!
Infine, mi è capitato di constatare che semplici processi siano eseguiti da alcuni motori e non da altri.
In particolare mi è capitato che un semplicissimo processo di test funzionasse tranquillamente su Apache ODE ma sul motore di Virtuoso non riuscivo nemmeno a fare il deploy.
Insomma, BPEL 2.0 è standard dal 2004, ma i motori non sono ancora abbastanza maturi.
Conclusioni
Dopo alcuni giorni spesi a fare sperimentazioni varie mi sono fatto l'idea che BPEL si presta bene per implementare processi semplici, che utilizzano tipi di dati base e con un'orchestrazione non troppo laboriosa. Però lo standard è stato ideato proprio per implementare orchestrazioni complesse..
Inoltre non è facile da imparare, soprattutto l'utilizzo degli editor grafici porta a dover trovare un tutorial per ogni cosa che si vuole fare, per capire dove si deve cliccare ecc..
Infine i tool ed i motori nonostante i molti sforzi ancora non raggiungono un buon livello per essere utilizzati in ambiente industriale, però, io ho utilizzato solo tool opensource, magari le soluzioni proprietarie funzionano meglio.
Nonostante tutto, trovo BPEL un ottimo strumento per la modellazione dei processi. Però, per la modellazione, non per l'esecuzione.
Trovo che si possano metter su orchestrazioni di servizi in modo molto più semplice utilizzando altre tecnologie, utilizzando i web service engine per esporli ai client.
Queste sono solo considerazioni mie personali, magari sono dovute alla mia poca esperienza al riguardo, ma ho notato che sono idee abbastanza condivise in rete.
Chissà, se mi capiterà di acquisire un po' di esperienza (e spero di no) magari capisco tutti gli errori che ho commesso e mi ricredo, però visti i risultati scarsi ottenuti dopo qualche giorno di sperimentazione ne dubito fortemente :)
Ok, direi che si può concludere questo post che sembra più uno sfogo personale.. :P
Alla prossima :)
Visualizzazione post con etichetta Web services. Mostra tutti i post
Visualizzazione post con etichetta Web services. Mostra tutti i post
venerdì 22 giugno 2012
BPEL e l'orchestrazione di servizi
Etichette:
Apache ODE,
BPEL,
Intalio,
SOA,
SOAP,
Virtuoso,
Web services,
XML
martedì 19 giugno 2012
Web services: request asincrone
Salve di nuovo :)
In questo post voglio parlare di come invocare un web service facendo richieste asincrone.
Partiamo dal presupposto di aver già implementato un servizio e di aver a disposizione il file WSDL che lo descrive.
Per la generazione del client ovviamente non si parte da zero, ma si può utilizzare il plugin per Eclipse WSDL2Java per generare la maggior parte del codice necessario.
Con questo tool si possono generare automaticamente le classi per fare request sincrone e asincrone contemporaneamente. Dandogli in pasto il WSDL del servizio da utilizzare, questo genera tutte le classi per eventuali tipi di dati custom che le operazioni del servizio accettano come parametri o che restituiscono.
Supponiamo che il servizio implementato sia MyService, con una sola operazione, echo, che prende in input una stringa e la restituisce. Il servizio non lancia eccezioni, per non complicare le cose :)
Le classi che vengono generate per il client sono:
L'implementazione del client che chiama il servizio è praticamente uguale ad un client sincrono, cambiano un paio di cose. I passi da seguire per richiamare il servizio sono:
E questo è quanto.
Ci sono giusto 2 cose da mettere in evidenza, riguardante il metodo startecho.
La prima è che, come si vede dal codice, oltre ai parametri dell'operazione esso ha bisogno di un'istanza del callback handler.
La seconda, molto importante, è che si deve tener presente che al contrario delle request sincrone, quando viene fatta una request asincrona, richiamando il metodo startOperation dello stub viene lanciato un nuovo thread e l'esecuzione del metodo chiamante continua parallelamente.
Per questo c'è bisogno di controllare che questo finisca. In questo caso molto semplicemente l'handler quando completa il suo lavoro setta un flag a true, ed il metodo chiamante non fa altro che ciclare e controllare ogni secondo se la richiesta è stata completata (magari l'echo non ha bisogno di aspettare "secondi".. :P).
Ed anche questo post è finito, era da un po' che non postavo qualcosa..
Per i prossimi post ancora non ho deciso che scriverò, ho un paio di idee..vedrò il tempo e la voglia cosa mi porteranno a fare :)
Alla prossima!
In questo post voglio parlare di come invocare un web service facendo richieste asincrone.
Partiamo dal presupposto di aver già implementato un servizio e di aver a disposizione il file WSDL che lo descrive.
Per la generazione del client ovviamente non si parte da zero, ma si può utilizzare il plugin per Eclipse WSDL2Java per generare la maggior parte del codice necessario.
Con questo tool si possono generare automaticamente le classi per fare request sincrone e asincrone contemporaneamente. Dandogli in pasto il WSDL del servizio da utilizzare, questo genera tutte le classi per eventuali tipi di dati custom che le operazioni del servizio accettano come parametri o che restituiscono.
Supponiamo che il servizio implementato sia MyService, con una sola operazione, echo, che prende in input una stringa e la restituisce. Il servizio non lancia eccezioni, per non complicare le cose :)
Le classi che vengono generate per il client sono:
- L'interfaccia MyService
- MyServiceResponse
- La classe astratta MyServiceCallbackHandler
- Echo (una classe per ogni operazione definita)
- MyService*Exception (una classe per ogni eccezione che può lanciare il servizio)
- MyServiceStub
Come per le richieste sincrone, la classe MyServiceStub viene utilizzata per settare i parametri della request e per invocare il servizio. La classe Echo (o qualsiasi altra classe corrispondente ad un'operazione) viene utilizzata per contenere i parametri dell'operazione, utilizzati poi dallo stub. Infine la classe MyServiceResponse ovviamente viene utilizzata per immagazzinare la risposta restituita dal servizio.
Per poter effettuare una request asincrona, è necessario utilizzare MyServiceCallbackHandler.
Questa è una classe astratta contenente dei metodi con implementazione vuota, uno per ogni operazione del servizio ed uno per ogni eccezione che questo può lanciare.
Quando la richiesta asincrona viene effettuata, viene lanciato un nuovo thread che si mette in attesa del completamento della request e viene eseguito il metodo associato all'operazione richiamata (o ad una condizione di eccezione) per gestire la risposta del servizio.
Infatti, tutti i metodi associati alle operazioni accettano come parametro un oggetto di tipo Response.
Invece, i metodi corrispondenti alle condizioni di eccezione, come si può immaginare, accettano un parametro di tipo Exception.
Bisogna quindi estendere tale classe ed implementare i metodi che ci interessano. Fortunatamente non c'è bisogno di implementarli tutti: i metodi di MyServiceCallbackHandler hanno tutti un'implementazione vuota, quindi se il nostro client utilizza una sola delle operazioni offerte da un servizio si può fare l'override dei metodi appropriati.
Supponiamo che la classe che implementa il callback handler si chiami CallbackImplementation (accorciamo un po il nome :P).
Giusto per scrivere un po' di codice riporto l'implementazione di questa classe :)
public class CallbackImplementation extends clientstubpackage.MyServiceCallbackHandler {
private boolean complete;
public CallbackImplementation() {
complete = false
}
@Override
public void receiveResultecho(
clientstubpackage.MyServiceResponse result
) {
System.out.println(result.getReturn());
complete = true;
}
}
L'implementazione del client che chiama il servizio è praticamente uguale ad un client sincrono, cambiano un paio di cose. I passi da seguire per richiamare il servizio sono:
- Istanziare un client stub
- Istanziare un oggetto per l'operazione da eseguire
- Istanziare un callback handler
- Settare eventuali parametri per l'operazione
- Richiamare il servizio con il metodo startOperation dello stub (invece di stub.operazione)
Ecco qui il codice che implementa il client ed esegue tutti i passi descritti:
public class ClientImplementation {
public static void main(String[] args) {
try {
//inizializzazione stub
MyServiceStub stub = new MyServiceStub();
//inizializzazione handler
CallbackImplementation handler = new CallbackImplementation();
//inizializzazione dell'oggetto per l'operazione e settaggio parametri
Echo echo = new Echo();
echo.setContent("Stringa da restituire");
long start = System.currentTimeMillis();
stub.startecho(echo, handler); //inizio della chiamata asincrona
while(!handler.hasCompleted()) {
Thread.sleep(1000);
long now = System.currentTimeMillis();
System.out.println("Waiting.. " + ((now - start)/1000));
}
System.out.println("Done!");
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
E questo è quanto.
Ci sono giusto 2 cose da mettere in evidenza, riguardante il metodo startecho.
La prima è che, come si vede dal codice, oltre ai parametri dell'operazione esso ha bisogno di un'istanza del callback handler.
La seconda, molto importante, è che si deve tener presente che al contrario delle request sincrone, quando viene fatta una request asincrona, richiamando il metodo startOperation dello stub viene lanciato un nuovo thread e l'esecuzione del metodo chiamante continua parallelamente.
Per questo c'è bisogno di controllare che questo finisca. In questo caso molto semplicemente l'handler quando completa il suo lavoro setta un flag a true, ed il metodo chiamante non fa altro che ciclare e controllare ogni secondo se la richiesta è stata completata (magari l'echo non ha bisogno di aspettare "secondi".. :P).
Ed anche questo post è finito, era da un po' che non postavo qualcosa..
Per i prossimi post ancora non ho deciso che scriverò, ho un paio di idee..vedrò il tempo e la voglia cosa mi porteranno a fare :)
Alla prossima!
Etichette:
Java,
Request Asincrone,
Web services,
WSDL
mercoledì 6 giugno 2012
Virtuoso Universal Server: un DBMS per tutto
Salve :)
In questo post volevo parlare di un DBMS che ho scoperto recentemente e ho trovato molto interessante.
Parlo di Virtuoso Universal Server (http://virtuoso.openlinksw.com/), un DBMS che, un po come tutti, promette di risolvere tutti i problemi del mondo :)
La particolarità di questo server è che effettivamente può fare un po' di tutto, nel senso che può essere usato sia come DBMS relazionale, sia come un NoSQL Database, come XML Store e come Triple Store.
Come DBMS relazionale offre un po' tutte le caratteristiche che offrono gli RDBMS tipici come MySQL e simili. Ovviamente supporta il linguaggio SQL, inoltre mette a disposizione molte funzioni utili.
Per quanto riguarda le sue funzionalità come XML Database, Virtuoso non è uno store nativo, cioè il documento XML non è visto come unità fondamentale di memorizzazione e i dati fisicamente non vengono memorizzato secondo la struttura di questi documenti. Infatti questi vengono memorizzati in tabelle SQL, come tipo di dato XML appunto. In pratica questo tipo di dato sarebbe un long varchar, ma utilizzando il tipo XML viene fatto anche il parsing dei documenti, in modo tale che, se durante un inserimento viene passato qualcosa che non è un documento ben formato, viene notificato direttamente un errore.
Ha diverse funzioni per l'utilizzo dei documenti XML e supporta XPath, XQuery e le trasformazioni XSLT.
Per quanto riguarda invece le sue funzionalità come Triple Store, il modello di dati utilizzato è il modello a grafo, che è quello utilizzato dalla maggior parte dei Triple Store che non si basano su database relazionali. In realtà non so il modello di dati fisico quale sia, ma il modello utilizzato per i dati in memoria è sicuramente a grafo.
E' possibile interrogarlo tramite il linguaggio SPARQL , eseguire update tramite SPARUL ed è possibile importare ed esportare dati in formato RDF. I formati di serializzazione RDF supportati sono HTML+RDFa, RDF-JSON, N3, Turtle, TriG, TriX e RDF/XML.
Virtuoso permette di essere utilizzato sia come SQL che NoSQL Database grazie alla virtualizzazione dei dati. Il modello fisico di memorizzazione è comune a tutti i motori DBMS, ma ognuno di essi vede i dati come se fossero memorizzati nel formato che più si adatta a loro.
La cosa penalizza le prestazioni?
In teoria potrebbe, ma in pratica leggendo in giro sembra sia abbastanza veloce. Personalmente l'ho provato solo su pochi dati e non ho eseguito nessuno stress test quindi non ho elementi per giudicare.
Come Triple Store sembra si comporti particolarmente bene. Ho visto diversi benchmark che lo confermano e anche in questa classifica W3C sembra si comporti abbastanza bene: http://www.w3.org/wiki/LargeTripleStores.
Questa immagine riassume un po la virtualizzazione dei dati e i vari moduli da cui è composto Virtuoso:
In questo post volevo parlare di un DBMS che ho scoperto recentemente e ho trovato molto interessante.
Parlo di Virtuoso Universal Server (http://virtuoso.openlinksw.com/), un DBMS che, un po come tutti, promette di risolvere tutti i problemi del mondo :)
La particolarità di questo server è che effettivamente può fare un po' di tutto, nel senso che può essere usato sia come DBMS relazionale, sia come un NoSQL Database, come XML Store e come Triple Store.
Come DBMS relazionale offre un po' tutte le caratteristiche che offrono gli RDBMS tipici come MySQL e simili. Ovviamente supporta il linguaggio SQL, inoltre mette a disposizione molte funzioni utili.
Per quanto riguarda le sue funzionalità come XML Database, Virtuoso non è uno store nativo, cioè il documento XML non è visto come unità fondamentale di memorizzazione e i dati fisicamente non vengono memorizzato secondo la struttura di questi documenti. Infatti questi vengono memorizzati in tabelle SQL, come tipo di dato XML appunto. In pratica questo tipo di dato sarebbe un long varchar, ma utilizzando il tipo XML viene fatto anche il parsing dei documenti, in modo tale che, se durante un inserimento viene passato qualcosa che non è un documento ben formato, viene notificato direttamente un errore.
Ha diverse funzioni per l'utilizzo dei documenti XML e supporta XPath, XQuery e le trasformazioni XSLT.
Per quanto riguarda invece le sue funzionalità come Triple Store, il modello di dati utilizzato è il modello a grafo, che è quello utilizzato dalla maggior parte dei Triple Store che non si basano su database relazionali. In realtà non so il modello di dati fisico quale sia, ma il modello utilizzato per i dati in memoria è sicuramente a grafo.
E' possibile interrogarlo tramite il linguaggio SPARQL , eseguire update tramite SPARUL ed è possibile importare ed esportare dati in formato RDF. I formati di serializzazione RDF supportati sono HTML+RDFa, RDF-JSON, N3, Turtle, TriG, TriX e RDF/XML.
Virtuoso permette di essere utilizzato sia come SQL che NoSQL Database grazie alla virtualizzazione dei dati. Il modello fisico di memorizzazione è comune a tutti i motori DBMS, ma ognuno di essi vede i dati come se fossero memorizzati nel formato che più si adatta a loro.
La cosa penalizza le prestazioni?
In teoria potrebbe, ma in pratica leggendo in giro sembra sia abbastanza veloce. Personalmente l'ho provato solo su pochi dati e non ho eseguito nessuno stress test quindi non ho elementi per giudicare.
Come Triple Store sembra si comporti particolarmente bene. Ho visto diversi benchmark che lo confermano e anche in questa classifica W3C sembra si comporti abbastanza bene: http://www.w3.org/wiki/LargeTripleStores.
Questa immagine riassume un po la virtualizzazione dei dati e i vari moduli da cui è composto Virtuoso:
Un'altra caratteristica molto interessante è che è molto semplice esporre le funzionalità di Virtuoso come web services. Infatti supporta può essere interrogato sia utilizzando SOAP che REST.
Si possono scrivere delle stored procedures e pubblicarle direttamente come servizi, con tanto di generazione automatica del WSDL.
Inoltre è possibile in qualche modo riutilizzare anche i web service scritti in Java, modificando un po il codice per eseguire una sorta di wrap. E' possibile riutilizzare anche i servizi scritti in linguaggi della piattaforma .NET.
Infine è disponibile un'estensione per poter utilizzare Virtuoso come motore BPEL4WS!
Grazie a questa è possibile organizzare le orchestrazioni dei servizi facendone il deploy direttamente nel DBMS.
Insomma, a mio parere le potenzialità sono molte, soprattutto negli scenari in cui si ha bisogno di dover gestire diversi tipi di dati ed avere la gestione migliore per ognuno di essi.
Inoltre poter gestire i dati, i servizi e magari anche le orchestrazioni da un unico punto semplifica parecchio la gestione..
Per concludere, Virtuoso è disponibile sia in versione commerciale che open source. La versione open source ovviamente ha qualche limitazione, non supporta infatti la replicazione dei dati e quello che chiamano "Virtual Database Engine", che permette di eseguire query SQL su database multipli remoti.
Personalmente ho fatto qualche sperimentazione con la versione opensource. Credo che sia molto più comodo utilizzarlo su sistemi Unix che non su Windows. Su sistemi come Ubuntu, per esempio, sono disponibili già i binari installabili dal gestore di pacchetti. Su Windows invece per poterlo compilare c'è bisogno di strimenti come Visual Studio ed altra roba..
Bene, direi che ho concluso con questo post pubblicitario :)
In questi giorni ho accantonato Virtuoso per fare delle prove con l'XML Database eXist. Se lo trovo interessante magari scriverò qualcosina anche su quello :)
Al prossimo post!
mercoledì 30 maggio 2012
Invio e ricezione di file da web services
Eccomi qui con un nuovo posto..
Ultimamente sto tenendo una bella media, chissà se riuscirò a tenere questa costanza.. :)
Bene, oggi volevo scrivere un post sull'invio e la ricezione di file verso e da un web service, visto che è una cosa che mi sono ritrovato a fare solo recentemente.
In questo post sia i servizi1 che i client saranno scritti in Java. Il web service engine che utilizzato è Apache Axis (installato su Tomcat 7) ed il protocollo di comunicazione è SOAP.
Per la generazione del pacchetto .aar contenente le classi per "deployare" il servizio in Axis è stato utilizzato il plugin per Eclipse "Service Archiver", mentre per creare il file WSDL dal codice del servizio e generare il client dal WSDL creato sono stati utilizzati Java2WSDL e WSDL2Java, entrambi contenuti nel plugin "Code Generator". Questi tool sono disponibili all'indirizzo: http://axis.apache.org/axis2/java/core/tools/index.html.
Prenderemo in considerazione un servizio, FileService che mette a disposizione 2 operazioni: UploadFile e GetFile.
La cosa importante affinchè le cose funzionino è definire la firma dei metodi corrispondenti alle operazioni affinchè il Code Generator possa generare un file WSDL con il giusto mapping tra i tipi.
Una prima prova potrebbe essere fatta specificando come parametro dell'operazione UploadFile e come tipo restituito dall'operazione GetFile semplicemente java.io.File.
Purtroppo questa non è una buona soluzione. Se si prova ad utilizzare Java2WSDL con i metodi così definiti, verrà generato un correttamente il file WSDL desiderato. Il problema, però, nasce quando si utilizza WSDL2Java per generare il client. Dalle classi create si vede che il mapping dei tipi è abbastanza sballato, infatti ci sono delle classi come java.io.xsd.File che per essere utilizzate come minimo dovrebbero essere incluse in un package diverso, in quanto l'uso del nome java.io per un package è ovviamente proibito.
Non so se sia possibile utilizzare comunque java.io.File per il trasferimento dei dati, probabilmente definendo a mano i tipi complessi nel WSDL e costruento i messaggi SOAP tramite codice è possibile, ma se uno non ha tempo, o non ha voglia, o una combinazione delle 2, allora sarebbe preferibile percorrere un'altra strada :)
Bene, dopo aver visto questa non-soluzione passiamo alle 2 che ho sperimentato:
Array di Byte
Questo metodo è abbastanza intuitivo, per fare l'upload di un file si passa un'array di byte che rappresenta il contenuto del file e il web service che lo riceve lo scrive su file. Lato client, però, non si può passare semplicemente un array di byte, ma dev'essere passato un DataHandler.
Il servizio può essere per esempio il seguente:
Con Java2WSDL si può creare il file che descrive il servizio e con WSDL2Java si può generare il client che lo interroga. Il client sarà composto da una serie di classi,tra cui ImportStub e ImportSingleFile che corrispondono rispettivamente al servizio ed all'operazione da richiamare.
Il client, per poter trasferire il file al servizio, dovrà:
E questo è quanto.
Il problema con questa soluzione è che i dati binari vengono codificati come testo per poi venire spediti in messaggi SOAP. Per ovviare a questo problema è possibile utilizzare MTOM.
MTOM e DataHandlers
MTOM (Message Transmission Optimization Mechanism) è una raccomandazione W3C per inviare e ricevere dati binari da web services. Altri meccanismi possono essere utilizzati per lo stesso scopo, come SwA (SOAP with Attachments) e i DIME Attachments, ma a MTOM sembra sia migliore di questi ultimi.
Per poterlo utilizzare in Axis è necessario abilitarlo: bisogna editare il file di configurazione axis/WEB-INF/conf/axis2.xml e settare a true il parametro enableMTOM.
Fatto ciò si può scrivere il servizio. In realtà cambia molto poco: invece di un array di byte come parametro prende un DataHandler. Ecco il codice:
Il client invece resta esattamente uguale.
E così si conclude questo post..spero di scrivere qualche altra cosa presto! :)
Ultimamente sto tenendo una bella media, chissà se riuscirò a tenere questa costanza.. :)
Bene, oggi volevo scrivere un post sull'invio e la ricezione di file verso e da un web service, visto che è una cosa che mi sono ritrovato a fare solo recentemente.
In questo post sia i servizi1 che i client saranno scritti in Java. Il web service engine che utilizzato è Apache Axis (installato su Tomcat 7) ed il protocollo di comunicazione è SOAP.
Per la generazione del pacchetto .aar contenente le classi per "deployare" il servizio in Axis è stato utilizzato il plugin per Eclipse "Service Archiver", mentre per creare il file WSDL dal codice del servizio e generare il client dal WSDL creato sono stati utilizzati Java2WSDL e WSDL2Java, entrambi contenuti nel plugin "Code Generator". Questi tool sono disponibili all'indirizzo: http://axis.apache.org/axis2/java/core/tools/index.html.
Prenderemo in considerazione un servizio, FileService che mette a disposizione 2 operazioni: UploadFile e GetFile.
La cosa importante affinchè le cose funzionino è definire la firma dei metodi corrispondenti alle operazioni affinchè il Code Generator possa generare un file WSDL con il giusto mapping tra i tipi.
Una prima prova potrebbe essere fatta specificando come parametro dell'operazione UploadFile e come tipo restituito dall'operazione GetFile semplicemente java.io.File.
Purtroppo questa non è una buona soluzione. Se si prova ad utilizzare Java2WSDL con i metodi così definiti, verrà generato un correttamente il file WSDL desiderato. Il problema, però, nasce quando si utilizza WSDL2Java per generare il client. Dalle classi create si vede che il mapping dei tipi è abbastanza sballato, infatti ci sono delle classi come java.io.xsd.File che per essere utilizzate come minimo dovrebbero essere incluse in un package diverso, in quanto l'uso del nome java.io per un package è ovviamente proibito.
Non so se sia possibile utilizzare comunque java.io.File per il trasferimento dei dati, probabilmente definendo a mano i tipi complessi nel WSDL e costruento i messaggi SOAP tramite codice è possibile, ma se uno non ha tempo, o non ha voglia, o una combinazione delle 2, allora sarebbe preferibile percorrere un'altra strada :)
Bene, dopo aver visto questa non-soluzione passiamo alle 2 che ho sperimentato:
- trasferire dati tramite array di byte e DataHandler
- trasferire dati tramite DataHandler utilizzando MTOM per gli allegati SOAP
Array di Byte
Questo metodo è abbastanza intuitivo, per fare l'upload di un file si passa un'array di byte che rappresenta il contenuto del file e il web service che lo riceve lo scrive su file. Lato client, però, non si può passare semplicemente un array di byte, ma dev'essere passato un DataHandler.
Il servizio può essere per esempio il seguente:
public class ImportService {
public String importSingleFile(String name, byte[] content) {
try {
File f = new File(name);
BufferedWriter out = new BufferedWriter(new FileWriter(f));
out.write(s);
out.close();
return "Import Successful!";
}
catch(IOException e) {
e.printStackTrace();
}
}
}
Con Java2WSDL si può creare il file che descrive il servizio e con WSDL2Java si può generare il client che lo interroga. Il client sarà composto da una serie di classi,tra cui ImportStub e ImportSingleFile che corrispondono rispettivamente al servizio ed all'operazione da richiamare.
Il client, per poter trasferire il file al servizio, dovrà:
- aprire il file che vuole trasferire
- associare il file ad un DataSource (presente in javax.activation)
- associare il DataSource ad un DataHandler (sempre in javax.activation)
- richiamare il servizio
public class Client {
public static void main(String args[]) {
try {
ImportStub stub = new ImportStub();
ImportSingleFile single = new ImportSingleFile();
File f = new File("upload.txt");
FileDataSource ds = new FileDataSource(f);
DataHandler dh = new DataHandler(ds);
single.setName(f.getName());
single.setContent(dh);
ImportSingleFileResponse res = stub.importSingleFile(single);
System.out.println(res.get_return());
}
catch(IOException | AxisFault e) {
e.printStackTrace();
}
}
}
E questo è quanto.
Il problema con questa soluzione è che i dati binari vengono codificati come testo per poi venire spediti in messaggi SOAP. Per ovviare a questo problema è possibile utilizzare MTOM.
MTOM e DataHandlers
MTOM (Message Transmission Optimization Mechanism) è una raccomandazione W3C per inviare e ricevere dati binari da web services. Altri meccanismi possono essere utilizzati per lo stesso scopo, come SwA (SOAP with Attachments) e i DIME Attachments, ma a MTOM sembra sia migliore di questi ultimi.
Per poterlo utilizzare in Axis è necessario abilitarlo: bisogna editare il file di configurazione axis/WEB-INF/conf/axis2.xml e settare a true il parametro enableMTOM.
Fatto ciò si può scrivere il servizio. In realtà cambia molto poco: invece di un array di byte come parametro prende un DataHandler. Ecco il codice:
public class ImportService { public String importSingleFile(String name, DataHandler dh) { try { File f = new File(name); FileOutputStream fos =new FileOutputStream(f);dh.writeTo(fos); fos.close();return "Import Successful!";} catch(IOException e) { e.printStackTrace(); } } }
Il client invece resta esattamente uguale.
E così si conclude questo post..spero di scrivere qualche altra cosa presto! :)
Iscriviti a:
Commenti (Atom)
