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!
Nessun commento:
Posta un commento