Serveis web SOAP amb Java EE 7

Veurem, mitjançant una aplicació d’exemple, els conceptes més rellevants dels serveis web SOAP. Aprendreu a crear-ne, a fer-ne el desplegament i a codificar diferents tipus de clients capaços de consumir els serveis web creats.

Els serveis web, ja siguin SOAP o REST, són una peça clau en el desenvolupament d’arquitectures de programari orientades a serveis (SOA per les sigles en anglès: Service Oriented Architecture) i, mes recentment, per a la creació d’arquitectures basades en microserveis.

SOAP (de l’anglès Simple Object Access Protocol) és un protocol que permet la interacció de serveis web basats en XML. L’especificació de SOAP inclou la sintaxi amb la qual s’han de definir els missatges, les regles de codificació dels tipus de dades i les regles de codificació que regiran les comunicacions entre aquests serveis web.

Quan es descriu una arquitectura SOAP ens referim a arquitectures basades en un conjunt de serveis que es despleguen a Internet mitjançant serveis web.

Un servei web és una tecnologia que fa servir un conjunt de protocols i estàndards per tal d’intercanviar dades entre aplicacions.

Podeu veure els serveis web com a components d’aplicacions distribuïdes que estan disponibles de forma externa i que es poden fer servir per integrar aplicacions escrites en diferents llenguatges (Java, .NET, PHP, etc.) i que s’executen en plataformes diferents (Windows, Linux, etc.). La seva característica principal és, doncs, la interoperativitat.

Un servei web publica una lògica de negoci exposada com a servei als clients. La diferencia més gran entre una lògica de negoci exposada com a servei web i, per exemple, una lògica de negoci exposada amb un mètode d’un EJB és que els serveis web SOAP proporcionen una interfície poc acoblada als clients. Això permet comunicar aplicacions que s’executin en diferents sistemes operatius, desenvolupades amb diferents tecnologies i amb diferents llenguatges de programació.

Els serveis web i, per tant, les aplicacions orientades a serveis es poden implementar amb diferents tecnologies. Les dues implementacions principals de serveis web que formen part de Java EE 7 són els serveis web SOAP i els serveis web RESTful.

Els serveis web SOAP són força més complexos que els serveis web RESTful, però proporcionen certes capacitats a nivell de seguretat i transaccionalitat que, a vegades, els fan l’única alternativa viable, sobretot en aplicacions empresarials.

En aquest capítol ens centrarem en la creació i el consum de serveis web SOAP amb Java EE 7.

El proveïdor del servei web SOAP el dissenya, l’implementa i en publica la seva interfície i el format dels missatges amb WSDL (de l’anglès Web Services Description Language), i els clients consulten aquesta definició i es comuniquen amb el servei web seguint la interfície i el format de missatges que ha publicat el proveïdor del servei en el document WDSL de definició del servei web.

WSDL descriu on es localitza un servei, quines operacions proporciona, el format dels missatges que han de ser intercanviats i com cal cridar el servei.

Opcionalment es pot publicar la definició WSDL del servei web en un registre UDDI (de l’anglès Universal Description, Dicovery and Integration) per tal que els clients puguin fer-hi cerques. Tingueu en compte que actualment no existeix un registre global que inclogui els diferents serveis web i, per això, UDDI ha passat a ser molt poc utilitzat a la pràctica. Normalment, el client coneix l’adreça del document WSDL de descripció del servei i, a partir d’aquest, invoca el servei web. Vegeu la relació entre els diferents components d’un servei web SOAP en la figura.

Quan parlem de serveis web, a les operacions se les sol anomenar endpoints. Al text farem servir indistintament qualsevol de les dues paraules.

Figura Components i relacions d’un servei web SOAP

L’especificació de Java EE 7 inclou diverses especificacions que donen complet suport a la creació i el consum de serveis web SOAP; la més destacada és JAX-WS (de l’anglès Java API for XML-Based Web Services), que utilitza missatges XML seguint el protocol SOAP i oculta la complexitat d’aquest protocol proporcionant una API senzilla per al desenvolupament, el desplegament i el consum de serveis web SOAP amb Java EE.

JAX-WS és l’API estàndard que defineix Java EE per desenvolupar i desplegar serveis web SOAP i permet ocultar la complexitat inherent a aquest protocol.

Tal com podeu veure en la figura, el runtime de JAX-WS converteix les crides a l’API en missatges SOAP i els missatges SOAP en crides a l’API i ho envia per HTTP. La implementació de JAX-WS que incorporen els servidors d’aplicacions compatibles amb Java EE 7 també proporciona eines per generar els documents WSDL de definició dels serveis web i eines per generar els artefactes que faran d’intermediaris entre el client i el servei web. Aquests artefactes s’anomenen stubs a la part del client i ties a la part del servei web.

Figura Esquema de comunicació entre un servei web SOAP i un client amb JAX-WS

Gestió de reserva de places a concerts com a servei web SOAP

Veurem els conceptes referents a la definició de serveis web SOAP amb Java EE 7 desenvolupant un servei web SOAP que permeti les següents operacions:

  • Proporcionar una llistat de concerts i el nombre de places lliures a cada un d’ells.
  • Fer la reserva d’una entrada per a un d’aquests concerts.
  • Cancel·lar una reserva d’una entrada.

Estratègia 'top-down'

Tot i ser força més feixuc, també podríem haver seguit una estratègia top-down, creant primer el document de WSDL de definició i, a partit d’aquest, generar automàticament el codi Java que l’implementi.

Farem servir una aplicació web ja desenvolupada que segueixi una arquitectura per capes i hi afegireu una capa de serveis on creareu i publicareu el servei web de gestió de reserves com a servei web SOAP.

Per a aquest exemple farem servir una aproximació bottom-up; crearem primer el codi Java que implementarà el servei web i, a partir d’aquest, es generarà automàticament el document WSDL que el descriurà.

Creació i configuració inicial del projecte

L’aplicació de la qual partirem s’anomena “Resentioc” i ens servirà per veure com podem exposar algunes funcionalitats de la capa de serveis d’una aplicació mitjançant serveis web SOAP. Es tracta d’un projecte Spring MVC senzill que segueix una arquitectura típica per capes per tal d’aconseguir una alta reusabilitat, un baix acoblament i una alta cohesió a l’aplicació.

El projecte constarà de quatre capes:

  • Capa de presentació
  • Capa de domini
  • Capa de serveis
  • Capa de persistència

A l’exemple no farem servir la capa de presentació, ja que l’objectiu no és proporcionar una interfície d’usuari a l’aplicació, sinó publicar els mètodes de negoci com a serveis web.

El model de domini ja el teniu implementat i és molt senzill, només hi ha una entitat Show que teniu en la figura i que representa els concerts que s’estan oferint amb el nombre d’entrades disponibles.

Figura Entitat Show

La capa de serveis serà la que treballarem i haurà de proporcionar un servei web SOAP que permeti als clients fer les següents operacions:

  • Llistat de concerts amb el nombre d’entrades disponibles per a cada un d’ells.
  • Reservar una entrada.
  • Anul·lar una reserva.

La capa de persistència també la teniu implementada i conté l’objecte repositori que permet mapar les dades de la font de dades amb l’objecte de domini. En el nostre cas, per simplificar, farem servir un repositori in memory que tindrà una llista precarregada de concerts.

Creació del servei web SOAP

L’objectiu d’aquest apartat és crear un servei web SOAP, seguint l’especificació JAX-WS, amb les tres operacions que corresponen a les tres funcionalitats que volem publicar:

  • Llistat de concerts amb el nombre d’entrades disponibles per cada un d’ells.
  • Reservar una entrada.
  • Anul·lar una reserva.

Creació dels serveis web

Primer ho farem “a mà”, sense gaire ajut de NetBeans, per tal que veieu i entengueu tot el procés. Veurem que NetBeans us pot ajudar força en el procés de creació dels serveis web, però és molt important que entengueu tot el procés abans de fer servir eines automatitzades.

Els serveis web SOAP amb Java EE 7 es poden implementar de dues maneres: amb una classe Java normal que s’executa en un contenidor de servlets o bé com un EJB de sessió sense estat o singleton que s’executa en un contenidor d’EJB. Veurem com s’implementa el servei web amb una classe Java normal.

Descarregueu el codi del projecte “Resentioc” en l’estat inicial d’aquest apartat des de l’enllaç que trobareu als annexos de la unitat i importeu-lo a NetBeans. Tot i que podeu descarregar-vos també el projecte en l’estat final des de un altre enllaç als annexos, sempre és millor que aneu fent vosaltres tots els passos partint del projecte en l’estat inicial de l’apartat.

Creeu una interfície que exposarà les tres operacions que volem; anomenarem la interfície TicketServiceEndpoint i la crearem al paquet cat.xtec.ioc.service.

A la interfície hi creeu els tres mètodes que volem exposar com a endpoints del servei web i els anoteu amb l’anotació javax.jws.WebMethod:

  1. package cat.xtec.ioc.service;
  2.  
  3. import cat.xtec.ioc.domain.Show;
  4. import java.util.ArrayList;
  5. import javax.jws.WebMethod;
  6. import javax.jws.WebService;
  7. import javax.jws.soap.SOAPBinding;
  8.  
  9. @WebService
  10. @SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
  11. public interface TicketServiceEndpoint {
  12. @WebMethod ArrayList<Show> getAllShows();
  13. @WebMethod Show makeReservation(String showId);
  14. @WebMethod Show cancelReservation(String showId);
  15. }

En les noves versions de Java EE, la creació explícita d’una interfície que caldrà que el servei web implementi és opcional.

Fixeu-vos que:

  • Hem anotat la interfície amb l’anotació @WebService per indicar que la interfície correspondrà a un servei web.
  • Hem anotat la interfície amb l’anotació @SOAPBindig per tal d’especificar l’estil de servei que volem crear. Per l’exemple, utilitzarem l’estil Document, que és l’opció per defecte.
  • Tots els mètodes que volem exposar cal que els anoteu amb l’anotació @WebMethod.

Un cop fet això cal que creeu la implementació del servei web; per fer-ho, creeu una nova classe anomenada TicketServiceEndpointImpl al paquet cat.xtec.ioc.service.impl. En aquesta classe cal que hi implementeu els tres mètodes que volem exposar com a endpoints del servei web:

  1. package cat.xtec.ioc.service.impl;
  2.  
  3. import cat.xtec.ioc.domain.Show;
  4. import cat.xtec.ioc.domain.repository.ShowRepository;
  5. import cat.xtec.ioc.domain.repository.impl.InMemoryShowRepository;
  6. import javax.jws.WebService;
  7. import cat.xtec.ioc.service.TicketServiceEndpoint;
  8. import java.util.ArrayList;
  9.  
  10.  
  11. @WebService(serviceName = "TicketService",
  12. endpointInterface = "cat.xtec.ioc.service.TicketServiceEndpoint")
  13. public class TicketServiceEndpointImpl implements TicketServiceEndpoint {
  14.  
  15. private final ShowRepository showRepository = new InMemoryShowRepository();
  16.  
  17. @Override
  18. public ArrayList<Show> getAllShows() {
  19. return new ArrayList<Show>(this.showRepository.getAllShows());
  20. }
  21.  
  22. @Override
  23. public Show makeReservation(String showId) {
  24. return this.showRepository.makeReservation(showId);
  25. }
  26.  
  27. @Override
  28. public Show cancelReservation(String showId) {
  29. return this.showRepository.cancelReservation(showId);
  30. }
  31. }

Hem anotat la classe amb l’anotació @WebService i li hem indicat amb l’atribut name que els clients faran referència al servei web amb el nom TicketService. També li hem indicat quina és la interfície que implementa el servei web amb la ruta completa de la interfície TicketServiceEndpoint:

  1. @WebService(serviceName = "TicketService",
  2. endpointInterface = "cat.xtec.ioc.service.TicketServiceEndpoint")
  3. public class TicketServiceEndpointImpl implements TicketServiceEndpoint {

La classe implementa la interfície que defineix el servei web i proporciona una implementació per a cada un dels tres mètodes que implementaran les operacions que publica el servei web cridant mètodes existents del repositori de concerts. A aquests mètodes no cal afegir-hi cap anotació especial; el fet d’implementar una interfície amb mètodes anotats amb @WebMethod ja indica quins mètodes de la classe corresponen als endpoints del servei web.

I aquesta és tota la feina que ens cal fer per implementar el servei web de reserva d’entrades, ara tan sols ens manca fer-ne el desplegament al servidor d’aplicacions i provar-lo.

Desplegament del servei web SOAP

Un cop creat el servei web cal que el desplegueu per tal de fer-lo accessible als clients. El procés de desplegament del servei web engega un conjunt de processos definits a JAX-WS per tal de generar el document WSDL de descripció del servei i publicar els endpoints del servei web.

El desplegament del servei web es pot fer de diverses maneres, entre elles:

  • Desplegant l’aplicació Java EE que el conté mitjançant els mecanismes normals de desplegament de qualsevol aplicació Java EE.
  • Creant una aplicació Java que s’encarregui de publicar el servei web a un URL determinat.

El desplegament del servei web com a part de l’aplicació Java EE que el conté és molt senzill, simplement cal que feu Run a NetBeans (vegeu la figura) i es farà el desplegament al servidor d’aplicacions que tingueu configurat per al projecte:

Figura ‘Run’ a NetBeans

En fer el desplegament, la implementació de JAX-WS que té el servidor d’aplicacions ja genera automàticament el document WSDL de definició del servei i publica els endpoints que heu definit amb el nom de servei que li heu donat (en el cas que ens ocupa l’hem anomenat TicketService). La figura mostra la sortida de la consola de NetBeans amb el desplegament del servei web:

Figura Desplegament a NetBeans

Podeu provar que el desplegament des de NetBeans ha anat bé consultant el document WSDL generat en el següent enllaç: localhost:8080/resentioc/TicketService?wsdl.

El desplegament mitjançant una aplicació Java que s’encarregui de publicar el servei web és una mica més complexa, i ens caldrà crear una classe Java que tingui un mètode main que cridi el mètode publish de l’objecte javax.xml.ws.Endpoint. Per fer-ho creem una classe TicketServicePublisher al paquet cat.xtec.ioc.publisher amb el següent codi:

  1. package cat.xtec.ioc.publisher;
  2.  
  3. import cat.xtec.ioc.service.impl.TicketServiceEndpointImpl;
  4. import javax.xml.ws.Endpoint;
  5.  
  6. public class TicketServicePublisher {
  7.  
  8. public static void main(String[] args) {
  9. Endpoint.publish("http://localhost:9999/publisher/TicketService", new TicketServiceEndpointImpl());
  10. }
  11. }

Fixeu-vos que al mètode publish de l’objecte javax.xml.Endpoint cal que li passeu l’URL on voleu publicar el servei web i una instància de la classe que implementa el servei web.

Amb això ja podeu publicar el servei web de reserva d’entrades executant aquesta aplicació Java; per fer-ho, poseu-vos damunt de la classe a NetBeans i feu Run File (vegeu la figura).

Figura ‘Run File’ a NetBeans

Si ho feu, veureu el següent error a la consola de NetBeans:

  1. Exception in thread "main" com.sun.xml.ws.model.RuntimeModelerException:
  2. runtime modeler error: Wrapper class cat.xtec.ioc.service.jaxws.GetAllShows is not found. Have you run APT to generate them?

Generar artefactes

Quan heu fet el desplegament amb NetBeans és el contenidor del servidor d’aplicacions qui s’encarrega de generar automàticament aquests artefactes, per això no ho heu hagut de fer manualment.

El problema és que ens cal executar una utilitat del JDK anomenada wsgen que generarà tots els artefactes portables que necessita el servei web per ser publicat i consumit.

Per fer-ho obriu un command-prompt, situeu-vos al directori \target\classes\ on tingueu el projecte “Resentioc” i executeu la següent línia (cal que tingueu el directori <JDK_HOME>/bin al classpath per tal que us trobi l’executable wsgen):

Amb Windows:

wsgen -verbose  -keep -cp . -s ..\..\src\main\java cat.xtec.ioc.service.impl.TicketServiceEndpointImpl

Amb Linux:

wsgen -verbose  -keep -cp . -s ../../src/main/java cat.xtec.ioc.service.impl.TicketServiceEndpointImpl

Aquesta instrucció us generarà un conjunt de fitxers .java amb tots els artefactes portables necessaris per fer la publicació des de l’aplicació Java i els col·locarà al paquet cat.xtec.ioc.service.jaxws del projecte “Resentioc”.

Ara sí que podeu publicar el servei web de reserva d’entrades executant el Publisher; si tot va bé, veureu que a la part inferior esquerra de NetBeans (vegeu la figura) us apareix una finestra indicant que el servei web està corrent a l’URL que heu especificat.

Figura Servei web publicat a NetBeans

Podeu provar que el desplegament des de l’aplicació Java ha anat bé consultant el document WSDL generat en el següent enllaç: localhost:9999/publisher/TicketService?wsdl.

Provant el servei web

Si tot ha anat bé ja teniu el servei web desplegat i a punt per provar-lo. Però com ho fem? Com el provem? Fixeu-vos que fins al moment no hem codificat cap client que faci peticions al servei web desenvolupat; com podem, doncs, provar-lo?

Per tal de provar el servei web que heu desenvolupat, desplegueu l’aplicació al servidor d’aplicacions Glassfish i accediu al següent URL: localhost:8080/resentioc/TicketService?Tester, on veureu una pàgina (vegeu la figura) amb un botó per a cada una de les operacions que conté el servei web i un enllaç al fitxer WSDL de definició de servei que s’ha generat:

Figura Pàgina de prova del servei web

Quan es desplega un servei web SOAP es genera un fitxer WSDL de definició del servei web. El fitxer WSDL està forma per elements XML que descriuen completament el servei web i com s’ha de consumir. El podeu consultar en el següent URL: localhost:8080/resentioc/TicketService?WSDL.

  1. <definitions targetNamespace="http://impl.service.ioc.xtec.cat/" name="TicketService">
  2. <import namespace="http://service.ioc.xtec.cat/" location="http://localhost:8080/resentioc/TicketService?wsdl=1"/>
  3. <binding name="TicketServiceEndpointImplPortBinding" type="ns1:TicketServiceEndpoint"></binding>
  4. <service name="TicketService">
  5. <port name="TicketServiceEndpointImplPort" binding="tns:TicketServiceEndpointImplPortBinding">
  6. <soap:address location="http://localhost:8080/resentioc/TicketService"/>
  7. </port>
  8. </service>
  9. </definitions>

El fitxer WSDL és un document XML que té una secció abstracta per definir els ports, els missatges i els tipus de dades de les operacions i una part concreta que defineix la instància concreta on hi ha aquestes operacions (vegeu la figura). Aquesta estructura permet reutilitzar la part abstracta del document.

Figura Estructura general d’un fitxer WSDL (versions 1.1 i 2.0)

A la part abstracta hi tenim els següents elements:

  • types
  • message
  • portType

Els tipus de dades, tant d’entrada com de sortida, de les operacions es defineixen amb XSD a l’etiqueta <types>. En el nostre cas veiem que ho tenim definit amb un import de localhost:8080/resentioc/TicketService?wsdl=1; si accediu a aquest fitxer veureu que referencia un document d’esquema definit amb XSD:

  1. <definitions targetNamespace="http://service.ioc.xtec.cat/">
  2. <types>
  3. <xsd:schema>
  4. <xsd:import namespace="http://service.ioc.xtec.cat/" schemaLocation="http://localhost:8080/resentioc/TicketService?xsd=1"/>
  5. </xsd:schema>
  6. </types>
  7. <message name="makeReservation"></message>
  8. <message name="makeReservationResponse"></message>
  9. <message name="getAllShows"></message>
  10. <message name="getAllShowsResponse"></message>
  11. <message name="cancelReservation"></message>
  12. <message name="cancelReservationResponse"></message>
  13. <portType name="TicketServiceEndpoint"></portType>
  14. </definitions>

I en aquest document XSD localhost:8080/resentioc/TicketService?xsd=1 és on trobem la definició de les operacions i els tipus de dades, tant d’entrada com de sortida, que fan servir les operacions:

  1. <xs:schema version="1.0" targetNamespace="http://service.ioc.xtec.cat/">
  2. <xs:element name="cancelReservation" type="tns:cancelReservation"/>
  3. <xs:element name="cancelReservationResponse" type="tns:cancelReservationResponse"/>
  4. <xs:element name="getAllShows" type="tns:getAllShows"/>
  5. <xs:element name="getAllShowsResponse" type="tns:getAllShowsResponse"/>
  6. <xs:element name="makeReservation" type="tns:makeReservation"/>
  7. <xs:element name="makeReservationResponse" type="tns:makeReservationResponse"/>
  8. <xs:complexType name="cancelReservation">
  9. <xs:sequence>
  10. <xs:element name="arg0" type="xs:string" minOccurs="0"/>
  11. </xs:sequence>
  12. </xs:complexType>
  13. <xs:complexType name="cancelReservationResponse">
  14. <xs:sequence>
  15. <xs:element name="return" type="tns:show" minOccurs="0"/>
  16. </xs:sequence>
  17. </xs:complexType>
  18. <xs:complexType name="show">
  19. <xs:sequence>
  20. <xs:element name="availableTickets" type="xs:int" minOccurs="0"/>
  21. <xs:element name="id" type="xs:string" minOccurs="0"/>
  22. <xs:element name="location" type="xs:string" minOccurs="0"/>
  23. <xs:element name="name" type="xs:string" minOccurs="0"/>
  24. </xs:sequence>
  25. </xs:complexType>
  26. <xs:complexType name="makeReservation">
  27. <xs:sequence>
  28. <xs:element name="arg0" type="xs:string" minOccurs="0"/>
  29. </xs:sequence>
  30. </xs:complexType>
  31. <xs:complexType name="makeReservationResponse">
  32. <xs:sequence>
  33. <xs:element name="return" type="tns:show" minOccurs="0"/>
  34. </xs:sequence>
  35. </xs:complexType>
  36. <xs:complexType name="getAllShows">
  37. <xs:sequence/>
  38. </xs:complexType>
  39. <xs:complexType name="getAllShowsResponse">
  40. <xs:sequence>
  41. <xs:element name="return" type="tns:show" minOccurs="0" maxOccurs="unbounded"/>
  42. </xs:sequence>
  43. </xs:complexType>
  44. </xs:schema>

Aconseguir interoperativitat

El runtime de JAX-WS serà l’encarregat de fer les transformacions dels missatges SOAP a crides a l’API Java, fent els mapatges de tipus adient per aconseguir, per exemple, tornar als clients objectes Java complexos de tipus Show. Aquesta és la clau per aconseguir interoperativitat, ja que un client que no sigui Java i que tingui un runtime que permeti fer les transformacions pertinents també podrà cridar el servei de gestió de reserves que heu creat.

Per exemple, si us hi fixeu, defineix que les peticions a l’operació cancelReservation reben un paràmetre de tipus string i a les respostes es torna un tipus de dades complex anomenat show que està format per un int i tres string (availableTickets, id, location i name)

Després de la definició dels tipus de dades trobem els elements <message> amb la definició dels missatges que es poden intercanviar les operacions del servei web, amb una entrada per a la petició i una altra per a la resposta:

  1. <definitions targetNamespace="http://service.ioc.xtec.cat/">
  2. <types></types>
  3. <message name="makeReservation"></message>
  4. <message name="makeReservationResponse"></message>
  5. <message name="getAllShows"></message>
  6. <message name="getAllShowsResponse"></message>
  7. <message name="cancelReservation"></message>
  8. <message name="cancelReservationResponse"></message>
  9. <portType name="TicketServiceEndpoint"></portType>
  10. </definitions>

I després, els elements <portType> ens defineixen les operacions que es poden fer al servei web i els seus paràmetres:

  1. <definitions targetNamespace="http://service.ioc.xtec.cat/">
  2. <types></types>
  3. <message name="makeReservation"></message>
  4. <message name="makeReservationResponse"></message>
  5. <message name="getAllShows"></message>
  6. <message name="getAllShowsResponse"></message>
  7. <message name="cancelReservation"></message>
  8. <message name="cancelReservationResponse"></message>
  9. <portType name="TicketServiceEndpoint">
  10. <operation name="makeReservation">
  11. <input ns1:Action="http://service.ioc.xtec.cat/TicketServiceEndpoint/makeReservationRequest" message="tns:makeReservation"/>
  12. <output ns2:Action="http://service.ioc.xtec.cat/TicketServiceEndpoint/makeReservationResponse" message="tns:makeReservationResponse"/>
  13. </operation>
  14. <operation name="getAllShows">
  15. <input ns3:Action="http://service.ioc.xtec.cat/TicketServiceEndpoint/getAllShowsRequest" message="tns:getAllShows"/>
  16. <output ns4:Action="http://service.ioc.xtec.cat/TicketServiceEndpoint/getAllShowsResponse" message="tns:getAllShowsResponse"/>
  17. </operation>
  18. <operation name="cancelReservation">
  19. <input ns5:Action="http://service.ioc.xtec.cat/TicketServiceEndpoint/cancelReservationRequest" message="tns:cancelReservation"/>
  20. <output ns6:Action="http://service.ioc.xtec.cat/TicketServiceEndpoint/cancelReservationResponse" message="tns:cancelReservationResponse"/>
  21. </operation>
  22. </portType>
  23. </definitions>

Vegem, per exemple, que l’operació cancelReservation rep com a paràmetre d’entrada un tns:cancelReservation i que retorna un tns:cancelReservationResponse. Aquests tipus de dades han estat definides completament en el document XSD (vegeu la figura).

Figura Tipus de dades de cancelReservation

A la part concreta hi tenim els següents elements:

  • binding
  • service

L’element <binding> (vegeu la figura) defineix el protocol i el format en què es poden fer les operacions; en el nostre cas, es faran per HTTP i amb un estil de Document.

Figura Element binding

L’element <service> (vegeu la figura), per la seva part, defineix el lloc físic on hi ha el servei web (adreça URL) mitjançant una col·lecció de punts de connexió <binding>. En el nostre cas, el servei web es cridarà amb el nom TicketService i està desplegat a l’adreça localhost:8080/resentioc/TicketService.

Figura Element service

Si voleu provar alguna de les operacions que proporciona el servei web, per exemple, getAllShows, polseu el botó corresponent (vegeu la figura) i es fa una petició a l’operació del servei web que torna la llista de tots els concerts amb les entrades disponibles.

Figura Botó per provar l’operació getAllShows

Aquesta petició genera aquest missatge SOAP de petició:

  1. <?xml version="1.0" encoding="UTF-8"?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  2. <SOAP-ENV:Header/>
  3. <S:Body>
  4. <ns2:getAllShows xmlns:ns2="http://service.ioc.xtec.cat/"/>
  5. </S:Body>
  6. </S:Envelope>

I aquest missatge SOAP de resposta:

  1. <?xml version="1.0" encoding="UTF-8"?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  2. <SOAP-ENV:Header/>
  3. <S:Body>
  4. <ns2:getAllShowsResponse xmlns:ns2="http://service.ioc.xtec.cat/">
  5. <return>
  6. <availableTickets>5</availableTickets>
  7. <id>1</id>
  8. <location>Palau Sant Jordi</location>
  9. <name>U2 360 Tour</name>
  10. </return>
  11. <return>
  12. <availableTickets>3</availableTickets>
  13. <id>2</id>
  14. <location>Palau de la musica</location>
  15. <name>Carmina Burana</name>
  16. </return>
  17. </ns2:getAllShowsResponse>
  18. </S:Body>
  19. </S:Envelope>

Fent servir la gestió de reserva de places a concerts des d'una aplicació Java 'stand-alone'

Hi ha dues maneres de cridar serveis web des de Java amb l’API JAX-WS:

  • Creant un stub estàtic.
  • Fent servir una interfície d’invocació dinàmica.

Anem a crear una aplicació Java stand-alone que consumeixi el servei web de gestió de reserves de places a concerts, i ho farem desenvolupant un client Java stand-alone que accedirà al servei web amb JAX-WS utilitzant stubs estàtics.

Tot i que podeu descarregar-vos el projecte en l’estat final d’aquest apartat en l’enllaç que trobareu als annexos de la unitat, sempre és millor que aneu fent vosaltres tots els passos partint del projecte en l’estat inicial de l’apartat. Recordeu que podeu utilitzar la funció d’importar per carregar els projectes a NetBeans.

Els passos generals per fer un client que faci servir stubs estàtics són els següents:

  1. Codificar la classe que farà de client.
  2. Generar els artefactes necessaris per poder consumir el servei web des d’aquest client amb la utilitat wsimport.
  3. Compilar i executar el client.

Descarregueu el codi del projecte “Resentclientioc” en l’estat inicial d’aquest apartat des de l’enllaç que trobareu als annexos de la unitat i importeu-lo a NetBeans.

Creeu la classe Java que farà de client al paquet cat.xtec.ioc.client i l’anomeneu TicketServiceClient.

Servidor i WSDL

Assegureu-vos que teniu el servidor d’aplicacions arrencat i el document WSDL de descripció del servei web de gestió de reserves de places a concerts accessible a l’URL que especifiqueu.

Genereu els artefactes per tal de poder consumir el servei web amb la utilitat wsimport. A wsimport cal especificar l’URL del document WSDL de descripció del servei web de gestió de reserves i on voleu que us generi els artefactes. Per fer-ho, obriu un command-prompt, situeu-vos al directori \target\classes\ on tingueu el projecte “Resentclientioc” i executeu la següent línia (cal que tingueu el directori <JDK_HOME>/bin al classpath per tal que us trobi l’executable wsimport i que hagueu fet Clean and Build del projecte):

Amb Windows:

wsimport -s ..\..\src\main\java -p cat.xtec.ioc.service.client.jaxws http://localhost:8080/resentioc/TicketService?wsdl

Amb Linux:

wsimport -s ../../src/main\java -p cat.xtec.ioc.service.client.jaxws http://localhost:8080/resentioc/TicketService?wsdl

Això us generarà un conjunt de fitxers .java amb tots els artefactes portables necessaris per cridar el servei web des de l’aplicació Java i els col·locarà al paquet cat.xtec.ioc.service.client.jaxws del projecte “Resentclientioc”.

Ara ja podem codificar la classe Java TicketServiceClient creada utilitzant els artefactes generats per wsimport. A les classes Java generades n’hi haurà una que s’anomenarà TicketService i que heretarà de Service; cal que instancieu aquesta classe i, a partir d’ella, obtingueu l’stub que us permetrà cridar les operacions del servei web.

El codi de la classe TicketServiceClient amb una crida a l’operació que mostra tots els concerts amb el nombre d’entrades disponibles és el següent:

  1. package cat.xtec.ioc.client;
  2.  
  3. import cat.xtec.ioc.service.client.jaxws.Show;
  4. import cat.xtec.ioc.service.client.jaxws.TicketService;
  5. import cat.xtec.ioc.service.client.jaxws.TicketServiceEndpoint;
  6. import java.util.List;
  7.  
  8. public class TicketServiceClient {
  9.  
  10. public static void main(String[] args) {
  11. TicketService service = new TicketService();
  12. TicketServiceEndpoint port = service.getTicketServiceEndpointImplPort();
  13. List<Show> list = port.getAllShows();
  14. printAllShows(list);
  15. }
  16.  
  17. public static void printAllShows(List<Show> shows) {
  18. shows.stream().forEach((show) -> {
  19. System.out.println("Show [id=" + show.getId() + ", name=" + show.getName() + ", available tickets=" + show.getAvailableTickets() + "]");
  20. });
  21. }
  22. }

Ara ja podem executar el client; per fer-ho, poseu-vos damunt de la classe TicketServiceClient i feu Run File a NetBeans (vegeu la figura).

Figura Execució del client Java a NetBeans

I obtindreu per consola un llistat dels concerts i de les entrades disponibles per a cada un d’ells:

Show [id=1, name=U2 360 Tour, available tickets=5]
Show [id=2, name=Carmina Burana, available tickets=3]

Fent servir la gestió de reserva de places a concerts des d'una aplicació web

Anem a crear una aplicació web que consumeixi el servei web de gestió de reserves de places a concerts.

Per fer-ho crearem una aplicació web molt senzilla que farà servir el servei web SOAP de gestió de reserva de places a concerts.

Creació i configuració inicial del projecte

Tot i que podeu descarregar-vos el projecte en l’estat final d’aquest apartat en l’enllaç que trobareu als annexos de la unitat, sempre és millor que aneu fent vosaltres tots els passos partint del projecte en l’estat inicial de l’apartat. Recordeu que podeu utilitzar la funció d’importar per carregar els projectes a NetBeans.

Desplegueu el servei web com a part de l’aplicació Java EE que el conté fent Clean and Build i després Run a NetBeans.

El primer que cal fer és desplegar el servei web de gestió de reserva de places a concerts al servidor.

Per desplegar el servei web de gestió de reserva de places a concerts al servidor, descarregueu el codi del projecte resentioc.zip en l’enllaç que trobareu als annexos de la unitat i importeu-lo a NetBeans.

Comproveu que el servei web està desplegat correctament accedint a l’URL localhost:8080/resentioc/TicketService?WSDL amb un navegador.

Un cop desplegat el servei web ens cal un altre projecte, que serà l’aplicació web que accedirà com a client al servei web.

Per implementar l’aplicació web que accedirà com a client al servei web, descarregueu el codi del projecte resentwebclientioc.zip en l’enllaç que trobareu als annexos de la unitat i importeu-lo a NetBeans.

No entrarem en detalls, però el projecte que tenim com a punt de partida és una senzilla aplicació web Spring MVC que ha de mostrar un llistat dels concerts disponibles i, per a cada concert, ha de proporcionar una acció que permeti reservar una entrada i una acció que permeti cancel·lar una reserva. Per simplicitat, a l’exemple no seguirem estrictament una arquitectura per capes on els serveis es cridin des de la capa de serveis. Si ho féssim així crearíem un servei propi en aquesta capa i aquest servei seria l’encarregat de cridar el servei web SOAP de reserva de places a concerts.

L’aplicació web utilitzarà el servei web SOAP de gestió de reserva de places a concerts per proporcionar aquestes funcionalitats.

Creació i prova del client web

Referències a l''endpoint' del servei

A l’exemple creem directament una instància de l’endpoint del servei (invocació programàtica); també es pot utilitzar l’anotació @WebServiceRef per tal que el contenidor injecti automàticament la instància del proxy (o stub) del client.

Un cop tingueu el projecte “Resentwebclientioc” carregat a NetBeans, el primer que farem serà llistar tots els concerts disponibles. Per fer-ho heu d’accedir a la classe ShowController del paquet cat.xtec.ioc.controller que fa de controlador i crear la referència al servei web SOAP que ens permetrà obtenir la llista de concerts:

  1. private final TicketService showsWebService=new TicketService();

Si ho feu, veureu que NetBeans indica que la classe no compila. Això és degut al fet que ens falta generar els artefactes de client per tal de poder consumir el servei web amb la utilitat wsimport. A wsimport cal especificar l’URL del document WSDL de descripció del servei web de gestió de reserves i on voleu que us generi els artefactes.

Per fer-ho, obriu un command-prompt, situeu-vos al directori \target\classes\ on tingueu el projecte “Resentwebclientioc” i executeu la següent línia (cal que tingueu el directori <JDK_HOME>/bin al classpath per tal que us trobi l’executable wsimport i que hagueu fet Clean and Build del projecte):

Amb Windows:

wsimport -s ..\..\src\main\java -p cat.xtec.ioc.service.client.jaxws http://localhost:8080/resentioc/TicketService?wsdl

Amb Linux:

wsimport -s ../../src/main\java -p cat.xtec.ioc.service.client.jaxws http://localhost:8080/resentioc/TicketService?wsdl

Això us generarà un conjunt de fitxers .java amb tots els artefactes portables necessaris per cridar el servei web des de l’aplicació i els col·locarà al paquet cat.xtec.ioc.service.client.jaxws del projecte “Resentwebclientioc”. Un cop fet això, i l’import corresponent al controlador, la classe ja us compilarà.

A l’exemple hem vist que sempre ens cal generar els artefactes de client per poder cridar el servei web SOAP, i hem vist com fer-ho amb la utilitat del JDK wsimport. Si feu servir Maven al projecte també ho podeu fer amb un plugin per a JAX-WS que configura un goal de Maven anomenat wsimport i que s’executa a la fase generate-sources de generació del codi font.

Objectiu de l'exemple

Tingueu en compte que l’objectiu de l’exemple és veure com podem accedir al servei web SOAP des d’una aplicació web i no la construcció de l’aplicació web en si. Per això hi haurà molts detalls propis de l’anatomia de l’aplicació que elidirem o explicarem amb poc detall.

Ja tenim la referència al servei web SOAP; ara crearem l’acció MVC que torni la llista de concerts. Per fer-ho, creeu un mètode anomenat shows amb el següent codi:

  1. @RequestMapping(value = "/shows", method = RequestMethod.GET)
  2. public ModelAndView shows(HttpServletRequest request, HttpServletResponse response)
  3. throws ServletException, IOException {
  4. ModelAndView modelview = new ModelAndView("shows");
  5. modelview.getModelMap().addAttribute("shows", showsWebService.getTicketServiceEndpointImplPort().getAllShows());
  6. return modelview;
  7. }

La part important d’aquest mètode és la crida al servei web SOAP:

  1. showsWebService.getTicketServiceEndpointImplPort().getAllShows();

Obtenim una referència al servei web i invoquem el mètode getAllShows, que ens tornarà la llista de concerts.

Per provar-ho cal desplegar l’aplicació “Resentwebclientioc” a Glassfish. Per fer-ho, feu Run a NetBeans i es farà el desplegament al servidor d’aplicacions que tingueu configurat per al projecte (vegeu la figura).

Figura ‘Run’ a NetBeans

Si tot ha anat bé i accediu a l’URL localhost:8080/resentwebclientioc/shows veureu la pàgina que mostra el llistat de concerts disponibles (vegeu la figura).

Figura Llistat de concerts

El procés per crear la funcionalitat que permeti fer una reserva i cancel·lar-la és el mateix: us cal crear l’acció corresponent al controlador i fer ús dels mètodes que proporciona el servei web SOAP de gestió de reserva de places a concerts per implementar-la.

El codi per a aquestes dues accions és el següent:

  1. @RequestMapping("/makeReservation")
  2. public ModelAndView makeReservation(@RequestParam("id") String id, HttpServletRequest request, HttpServletResponse response)
  3. throws ServletException, IOException {
  4. showsWebService.getTicketServiceEndpointImplPort().makeReservation(id);
  5. ModelAndView modelview = new ModelAndView("shows");
  6. modelview.getModelMap().addAttribute("shows", showsWebService.getTicketServiceEndpointImplPort().getAllShows());
  7. return modelview;
  8. }
  9.  
  10. @RequestMapping("/cancelReservation")
  11. public ModelAndView cancelReservation(@RequestParam("id") String id, HttpServletRequest request, HttpServletResponse response)
  12. throws ServletException, IOException {
  13. showsWebService.getTicketServiceEndpointImplPort().cancelReservation(id);
  14. ModelAndView modelview = new ModelAndView("shows");
  15. modelview.getModelMap().addAttribute("shows", showsWebService.getTicketServiceEndpointImplPort().getAllShows());
  16. return modelview;
  17. }

Si desplegueu la nova versió de l’aplicació fent Run a NetBeans ja podreu provar les funcionalitat que permeten fer una reserva i cancel·lar-la.

Si ho proveu i aneu fent reserves per al concert del grup U2 veureu que, quan ja no quedin entrades disponibles, el servidor torna un error HTTP 500 (vegeu la figura).

Figura Error 500 - No more tickets available!

Això és degut al fet que des del codi del servei web, quan volem fer una reserva per a un concert que no té localitats disponibles, s’està llençant una excepció:

  1. public void makeTicketReservation() {
  2. if(this.availableTickets > 0) {
  3. this.availableTickets--;
  4. } else {
  5. throw new IllegalArgumentException("No more tickets available!");
  6. }
  7. }

Si mirem el log del servidor Glassfish veurem aquesta excepció:

Grave:   No more tickets available!
java.lang.IllegalArgumentException: No more tickets available!
  at cat.xtec.ioc.domain.Show.makeTicketReservation(Show.java:64)
	at cat.xtec.ioc.domain.repository.impl.InMemoryShowRepository.makeReservation(InMemoryShowRepository.java:33)
	at cat.xtec.ioc.service.impl.TicketServiceEndpointImpl.makeReservation(TicketServiceEndpointImpl.java:24)

JAX-WS converteix automàticament les excepcions de Java en una SOAPFault en format XML que viatjarà al missatge SOAP de resposta.

SOAPFault és el mecanisme que proporciona SOAP per indicar que el servei web cridat ha tingut algun problema.

Què s'ha après?

Heu vist les bases per al desenvolupament dels serveis web SOAP amb Java EE 7 i les heu treballat de forma pràctica mitjançant exemples.

Concretament, heu après:

  • Les nocions bàsiques dels serveis web SOAP amb Java EE.
  • A desenvolupar, desplegar i provar un servei web SOAP amb Java EE.
  • A desenvolupar i provar un client Java que consulti un servei web SOAP.
  • A desenvolupar i provar una aplicació web que consulti un servei web SOAP.

Per aprofundir en aquests conceptes i veure com us pot ajudar NetBeans en la creació i el consum de serveis web SOAP us recomanem que feu les activitats associades a aquest apartat.

Anar a la pàgina anterior:
Referències
Anar a la pàgina següent:
Activitats