Serveis web SOAP amb Spring Web Services

Mitjançant una aplicació d’exemple, veurem els conceptes més rellevants dels serveis web SOAP desenvolupats amb Spring Web Services (Spring-WS). 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) i, més recentment, per a la creació d’arquitectures basades en microserveis.

Quan es descriu una arquitectura SOA 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 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 interoperabilitat.

Un servei web publica una lògica de negoci exposada com a servei als clients. La diferència 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ó.

Spring és un bastiment (framework, en anglès) amb mòduls basat en Java Enterprise Edition. La principal característica del seu core és la utilització del patró de disseny d’inversió de control (de l’anglès Inversion of Control) i també la injecció de dependències (de l’anglès Dependency Injection), un tipus d’IoC. Spring ofereix també un conjunt de projectes/mòduls per desenvolupar serveis web SOAP i RESTful.

Els serveis web i, per tant, les aplicacions orientades a serveis es poden implementar amb diferents tecnologies. Tant Java EE 7 com Spring proporcionen implementacions per treballar amb serveis web SOAP i serveis web RESTful.

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.

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.

Spring també proporciona un projecte anomenat Spring-WS (de l’anglès Spring Web Services) enfocat a la creació i el consum de serveis web SOAP. Spring-WS es basa completament en Spring i porta inherent al seu model molts dels conceptes clau a Spring, com poden ser la inversió del control i la injecció de dependències.

Spring-WS és el projecte que ofereix Spring per desenvolupar i desplegar serveis web SOAP.

JAX-WS no pressuposa un model de desenvolupament per als serveis web i el desenvolupador pot triar tant una estratègia Code First (també anomenada Contract Last) com una estratègia Contract First, tot i que el més habitual és utilitzar l’estratègia Code First.

Spring-WS, en canvi, només suporta la creació de serveis web SOAP seguint una estratègia Contract First.

Per què 'Contract First'?

Si voleu saber les motivacions que té Spring per suportar tan sols una estratègia Contract First ho podeu fer consultant aquesta URL: bit.ly/2niObVu.

WSDL és un document XML que descriu un servei web SOAP. Descriu on es localitza un servei, quines operacions proporciona, el format dels missatges que han de ser intercanviats i com cal cridar el servei.

Quan parlem de serveis web, una estratègia Code First vol dir crear primer les classes Java que implementaran el servei i, a partir d’aquestes, generar els documents WSDL de definició del servei.

Una estratègia Contract First vol dir exactament el contrari: fer primer la definició del servei abans d’implementar-lo. Això vol dir descriure els paràmetres i tipus de retorn del servei amb XSD (de l’anglès XML Schema Definitions), després utilitzar aquest XSD per generar el document WSDL que serà el contracte públic del servei i, finalment, generar les classes Java que implementaran el servei d’acord amb aquest contracte.

L’elecció entre JAX-WS i Spring-WS a l’hora de desenvolupar serveis web SOAP amb Java no és senzilla i no hi ha cap de les dues tecnologies que sigui una clara guanyadora; alguns dels motius per utilitzar Spring-WS són:

  • Per disseny, una estratègia Contract First promou un baix acoblament entre el contracte i la implementació del servei web.
  • Suport més ampli amb les llibreries de tractament XML.
  • Suport més ampli i flexible en els mapatges entre XML i objectes Java.
  • Permet reutilitzar tot el coneixement que tingueu en Spring.
  • Suporta WS-Security i permet integrar-ho amb Spring Security.

Escrivint un servei web SOAP de consulta de dades d’empreses amb Spring-WS

Veurem els conceptes referents a la definició de serveis web SOAP amb Spring-WS desenvolupant un servei web SOAP amb Spring-WS que permeti consultar les dades d’una empresa per CIF.

Spring-WS ens obliga a utilitzar una estratègia Contract First per crear serveis web SOAP, això vol dir que primer descriurem els paràmetres i tipus de retorn del servei amb XSD, després utilitzarem aquest XSD per generar el document WSDL que serà el contracte públic del servei i, finalment, generarem les classes Java que implementaran el servei d’acord amb aquest contracte.

Quan desenvolupeu un servei amb una estratègia Contract First us heu de centrar a concretar el XML que definirà el servei, el codi Java que ho implementarà passa a segon terme!

Creació i configuració inicial del projecte

El projecte ja té el pom.xml preparat per poder desenvolupar el servei web amb Spring-WS; concretament, s’hi ha afegit aquestes dues dependències:

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

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-ws</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>wsdl4j</groupId>
  7. <artifactId>wsdl4j</artifactId>
  8. </dependency>

Farem el desplegament del servei web creant una aplicació executable. Per fer el desplegament creant una aplicació executable que s’executarà al contenidor de servlets que Spring porta incorporat cal crear una classe Java anotada amb @SpringBootApplication amb un mètode main que cridi el mètode run de SpringApplication. Per fer-ho ja us hem creat la classe Application al paquet cat.xtec.ioc.service.impl amb el següent codi:

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.

  1. package cat.xtec.ioc.service.impl;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5.  
  6. @SpringBootApplication
  7. public class Application {
  8.  
  9. public static void main(String[] args) {
  10. SpringApplication.run(Application.class, args);
  11. }
  12. }

El fet d’anotar la classe amb @SpringBootApplication afegeix tota la configuració necessària per tal que l’aplicació pugui funcionar de forma autònoma.

Creació del servei web SOAP

El servei web SOAP que volem crear és molt senzill, tan sols ens ha de permetre consultar la informació d’una empresa d’un repositori d’empreses.

Com que crearem el servei web seguint una estratègia Contract First, el primer que ens cal fer és crear el document XSD que definirà el model de domini. El model de domini es defineix amb un fitxer d’esquema XML que després Spring-WS s’encarregarà d’exportar a WSDL.

La part principal del nostre model del domini serà l’entitat Company, que representarà les dades d’una empresa i la modelarem amb un document XSD. Per fer-ho creeu un fitxer anomenat company.xsd al directori /src/main/resources/ amb el següent contingut:

  1. <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://cat.xtec.ioc/domain/company"
  2. targetNamespace="http://cat.xtec.ioc/domain/company" elementFormDefault="qualified">
  3.  
  4. <xs:complexType name="company">
  5. <xs:sequence>
  6. <xs:element name="cif" type="xs:string"/>
  7. <xs:element name="name" type="xs:string"/>
  8. <xs:element name="employees" type="xs:int"/>
  9. <xs:element name="email" type="xs:string"/>
  10. <xs:element name="web" type="xs:string"/>
  11. </xs:sequence>
  12. </xs:complexType>
  13.  
  14. </xs:schema>

Fixeu-vos que es defineix un tipus XML complex per construir el model del domini; en aquest cas, un objecte company que conté el CIF, el nom, el nombre de treballadors, el correu i la pàgina web de l’empresa.

Modularitat en XSD

Es podria definir l’entitat del domini i les operacions en un únic document XSD, però és molt més modular fer-ho en documents separats i utilitzar la capacitat de fer importacions que proporciona XSD.

Un cop definida la nostra entitat del domini ens cal concretar els tipus de les peticions i les respostes del servei web que permetrà consultar la informació de l’empresa per CIF. Per fer-ho creeu un fitxer anomenat companyoperations.xsd al directori /src/main/resources/ amb el següent contingut:

  1. <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://cat.xtec.ioc/domain/company/services"
  2. targetNamespace="http://cat.xtec.ioc/domain/company/services" xmlns:company="http://cat.xtec.ioc/domain/company" elementFormDefault="qualified">
  3. <xs:import namespace="http://cat.xtec.ioc/domain/company" schemaLocation="company.xsd"/>
  4. <xs:element name="getCompanyRequest">
  5. <xs:complexType>
  6. <xs:sequence>
  7. <xs:element name="cif" type="xs:string"/>
  8. </xs:sequence>
  9. </xs:complexType>
  10. </xs:element>
  11.  
  12. <xs:element name="getCompanyResponse">
  13. <xs:complexType>
  14. <xs:sequence>
  15. <xs:element name="company" type="company:company"/>
  16. </xs:sequence>
  17. </xs:complexType>
  18. </xs:element>
  19.  
  20. </xs:schema>

Fixeu-vos que:

  • S’importa la definició del tipus complex company que ha de tornar la resposta.
  • Es defineix la petició com un tipus XML complex que conté la cadena on passarem el CIF de l’empresa de la qual volem consultar les dades.
  • Es defineix la resposta també com un tipus XML complex que conté la informació de l’empresa consultada.

Per tal d’utilitzar els tipus que hem definit als fitxers XSD ens cal generar les classes Java per a aquests tipus. Spring-WS serà capaç de generar la classe Java Company que representa el model de domini i les classes Java que modelaran la petició (GetCompanyRequest) i la resposta (GetCompanyResponse) del servei web.

JAXB (de l’anglès Java Architecture for XML Binding) és un bastiment que permet fer la conversió entre documents XML i objectes Java, i a l’inrevés.

Aquesta és una part fonamental als serveis web SOAP: la conversió dels missatges SOAP d’XML cap a Java i a l’inrevés. Aquesta tasca no seria senzilla si l’haguéssiu de fer a mà, però Spring ho fa fàcil utilitzant el bastiment (framework, en anglès) JAXB, i ho farem en temps de construcció del projecte.

Això es pot fer de diverses formes; una manera senzilla és que ho faci el plugin de Maven jaxb-maven-plugin en temps de construcció del projecte. Afegiu les següents línies al fitxer pom.xml:

  1. <plugin>
  2. <groupId>org.codehaus.mojo</groupId>
  3. <artifactId>jaxb2-maven-plugin</artifactId>
  4. <version>1.6</version>
  5. <executions>
  6. <execution>
  7. <id>xjc</id>
  8. <goals>
  9. <goal>xjc</goal>
  10. </goals>
  11. </execution>
  12. </executions>
  13. <configuration>
  14. <schemaDirectory>${project.basedir}/src/main/resources/</schemaDirectory>
  15. <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
  16. <clearOutputDir>false</clearOutputDir>
  17. </configuration>
  18. </plugin>

Si recarregueu el pom.xml fent clic amb el botó dret damunt el nom del projecte i prement l’opció Reload POM del menú contextual i després feu Clean and Build, també al menú contextual de NetBeans, veureu com a la fase de generació el plugin xjc ha generat, a partir dels documents XSD de definició de servei, la classe Java que modela el domini i les classes Java que modelen la petició i la resposta del servei web (vegeu la figura).

Figura Classes Java generades

--- jaxb2-maven-plugin:1.6:xjc (xjc) @ springsoapemptioc ---
Generating source...
Analizando un esquema...
Compilando un esquema...
ioc\xtec\cat\domain\company\services\GetCompanyRequest.java
ioc\xtec\cat\domain\company\services\GetCompanyResponse.java
ioc\xtec\cat\domain\company\services\ObjectFactory.java
ioc\xtec\cat\domain\company\services\package-info.java
ioc\xtec\cat\domain\company\Company.java
ioc\xtec\cat\domain\company\ObjectFactory.java
ioc\xtec\cat\domain\company\package-info.java

Ara toca crear el repositori d’on el servei ha de consultar la informació de les empreses. En el nostre cas, per simplicitat, farem servir un repositori in memory que tindrà una llista precarregada d’empreses i un mètode findCompany que permet consultar una de les empreses per CIF. Òbviament, en un projecte real la definició del servei serà molt més complexa!

Per fer-ho creeu una classe Java anomenada InMemoryCompanyRepository al paquet cat.xtec.ioc.domain.repository.impl amb el següent codi:

  1. package cat.xtec.ioc.domain.repository.impl;
  2.  
  3. import ioc.xtec.cat.domain.company.Company;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import javax.annotation.PostConstruct;
  7. import org.springframework.stereotype.Repository;
  8. import org.springframework.util.Assert;
  9.  
  10. public class InMemoryCompanyRepository {
  11.  
  12. private static final List<Company> companies = new ArrayList<Company>();
  13.  
  14. @PostConstruct
  15. public void initData() {
  16. Company oracle = new Company();
  17. oracle.setCif("XXXXXXXX");
  18. oracle.setName("Oracle");
  19. oracle.setEmployees(5000);
  20. oracle.setEmail("oracle@oracle.com");
  21. oracle.setWeb("http://www.oracle.com");
  22.  
  23. companies.add(oracle);
  24.  
  25. Company ms = new Company();
  26. ms.setCif("YYYYYYYY");
  27. ms.setName("Microsoft");
  28. ms.setEmployees(10000);
  29. ms.setEmail("microsoft@microsoft.com");
  30. ms.setWeb("http://www.microsoft.com");
  31.  
  32. companies.add(ms);
  33.  
  34. Company redhat = new Company();
  35. redhat.setCif("ZZZZZZZZ");
  36. redhat.setName("Red Hat");
  37. redhat.setEmployees(2000);
  38. redhat.setEmail("redhat@redhat.com");
  39. redhat.setWeb("http://www.redhat.com");
  40. companies.add(redhat);
  41. }
  42.  
  43. public Company findCompany(String cif) {
  44. Assert.notNull(cif);
  45. Company result = null;
  46. for (Company company : companies) {
  47. if (cif.equals(company.getCif())) {
  48. result = company;
  49. }
  50. }
  51.  
  52. return result;
  53. }
  54. }

Amb tot això ja estem preparats per crear l’endpoint del servei web SOAP que tornarà la informació de les empreses. Noteu que, per sota, hi ha un servlet que s’encarrega de recollir les peticions SOAP que vagin al servei i les envia a l’endpoint per tal de processar-les.

Creeu una classe Java anomenada CompanyEndpoint al paquet cat.xtec.ioc.service.impl amb el següent codi:

  1. package cat.xtec.ioc.service.impl;
  2.  
  3. import cat.xtec.ioc.domain.repository.impl.InMemoryCompanyRepository;
  4. import ioc.xtec.cat.domain.company.services.GetCompanyRequest;
  5. import ioc.xtec.cat.domain.company.services.GetCompanyResponse;
  6.  
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.ws.server.endpoint.annotation.Endpoint;
  9. import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
  10. import org.springframework.ws.server.endpoint.annotation.RequestPayload;
  11. import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
  12.  
  13. @Endpoint
  14. public class CompanyEndpoint {
  15.  
  16. private static final String NAMESPACE_URI = "http://cat.xtec.ioc/domain/company/services";
  17.  
  18. private InMemoryCompanyRepository companyRepository;
  19.  
  20. @Autowired
  21. public CompanyEndpoint(InMemoryCompanyRepository companyRepository) {
  22. this.companyRepository = companyRepository;
  23. }
  24.  
  25. @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCompanyRequest")
  26. @ResponsePayload
  27. public GetCompanyResponse getCompany(@RequestPayload GetCompanyRequest request) {
  28. GetCompanyResponse response = new GetCompanyResponse();
  29. response.setCompany(companyRepository.findCompany(request.getCif()));
  30.  
  31. return response;
  32. }
  33. }

Fixeu-vos que:

  • Hem injectat el repositori d’empreses al constructor del servei amb Spring.
  • Hem anotat la classe amb @Endpoint per indicar a Spring que aquesta classe correspon a un endpoint d’un servei web SOAP i tindrà mètodes que serviran peticions SOAP. @Endpoint és una versió especialitzada de l’anotació @Component.
  • TARGET_NAMESPACE representa l’espai de noms que heu definit anteriorment al document XSD. Es fa servir per mapar les peticions a mètodes específics de l’endpoint.
  • Hem anotat el mètode getCompany amb l’anotació @PayloadRoot on es defineix l’espai de noms i el mètode que servirà les peticions de cerca d’empreses per CIF.
  • El missatge SOAP que consulti una empresa per CIF haurà de fer referència al PayloadRoot especificat.
  • Hem anotat el mètode amb @ResponsePayload per indicar que el mètode tornarà un objecte de tipus GetCompanyResponse amb la informació de l’empresa. Spring s’encarregarà de convertir aquest objecte a XML.
  • El paràmetre del mètode l’hem anotat amb @RequestPayload per indicar que el missatge d’entrada serà de tipus GetCompanyRequest. Spring s’encarregarà de convertir el missatge XML d’entrada a un objecte d’aquest tipus.
  • Tant el tipus de retorn com el paràmetre corresponen a classes generades automàticament en el procés de construcció a partir de la definició del fitxer d’esquema XML companyoperations.xsd.

Un cop creat l’endpoint del servei web SOAP tan sols ens queda configurar Spring per carregar tota la configuració. Això ho podeu fer de diverses maneres, ja sigui amb fitxers de configuració o amb anotacions a classes Java de configuració. A l’exemple utilitzarem aquesta darrera tècnica.

Creeu una classe Java anomenada WebServiceConfig al paquet cat.xtec.ioc.service.impl amb el següent codi:

  1. package cat.xtec.ioc.service.impl;
  2.  
  3. import org.springframework.boot.web.servlet.ServletRegistrationBean;
  4. import org.springframework.context.ApplicationContext;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.ComponentScan;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.core.io.ClassPathResource;
  9. import org.springframework.ws.config.annotation.EnableWs;
  10. import org.springframework.ws.config.annotation.WsConfigurerAdapter;
  11. import org.springframework.ws.transport.http.MessageDispatcherServlet;
  12. import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
  13. import org.springframework.xml.xsd.SimpleXsdSchema;
  14. import org.springframework.xml.xsd.XsdSchema;
  15.  
  16. @EnableWs
  17. @Configuration
  18. @ComponentScan("cat.xtec.ioc.service.imp, cat.xtec.ioc.domain.repository.impl")
  19. public class WebServiceConfig extends WsConfigurerAdapter {
  20.  
  21. @Bean
  22. public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
  23. MessageDispatcherServlet servlet = new MessageDispatcherServlet();
  24. servlet.setApplicationContext(applicationContext);
  25. servlet.setTransformWsdlLocations(true);
  26. return new ServletRegistrationBean(servlet, "/soapws/*");
  27. }
  28.  
  29. @Bean(name = "companyoperations")
  30. public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema companiesSchema) {
  31. DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
  32. wsdl11Definition.setPortTypeName("CompaniesPort");
  33. wsdl11Definition.setLocationUri("/soapws");
  34. wsdl11Definition.setTargetNamespace("http://cat.xtec.ioc/domain/company/services");
  35. wsdl11Definition.setSchema(companiesSchema);
  36. return wsdl11Definition;
  37. }
  38.  
  39. @Bean
  40. public XsdSchema companiesSchema() {
  41. return new SimpleXsdSchema(new ClassPathResource("companyoperations.xsd"));
  42. }
  43.  
  44. @Bean(name = "company")
  45. public XsdSchema companySchema() {
  46. return new SimpleXsdSchema(new ClassPathResource("company.xsd"));
  47. }
  48. }

Fixeu-vos que:

  • Spring fa servir un servlet especial de tipus MessageDispatcherServlet per servir peticions SOAP, i cal que el registreu al ApplicationContext de l’aplicació.
  • El mètode defaultWsdl11Definition s’encarrega de configurar el document WSDL de definició del servei web a partir del document XSD especificat.
  • L’ús de DefaultWsdl11Definition permet la generació automàtica del document WSDL.
  • El mètode defaultWsdl11Definition també defineix l’URI i l’espai de noms que caldrà que utilitzeu per cridar el servei web i que estarà disponible en el document WSDL de definició del servei.

Aquesta és tota la feina que ens cal fer per implementar el servei web de consulta de dades d’empreses; ara tan sols ens manca fer el desplegament 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 desplegament del servei web es pot fer de diverses maneres, entre les quals:

  • Desplegant l’aplicació Java EE que el conté mitjançant els mecanismes normals de desplegament de qualsevol aplicació Java EE.
  • Creant una aplicació executable que s’executarà amb un contenidor de servlets Tomcat que Spring porta incorporat.

Farem el desplegament del servei web creant una aplicació executable. Per fer el desplegament creant una aplicació executable que s’executarà al contenidor de servlets que Spring porta incorporat tan sols ens cal una classe Java anotada amb @SpringBootApplication amb un mètode main que cridi el mètode run de SpringApplication. Al projecte inicial ja teniu una classe Application al paquet cat.xtec.ioc.service.impl que permet que l’aplicació pugui funcionar de forma autònoma.

Prerequisits per a l'execució

Abans d’executar la classe cal que us assegureu de tenir el servidor Glassfish parat, ja que, si no, no podrà arrencar el servei Tomcat on desplegarem l’aplicació per un conflicte amb els ports. Tant Glassfish com Tomcat estan configurats per utilitzar el port 8080 per defecte.

Poseu-vos damunt la classe Application, feu clic amb el botó dret del ratolí i seleccioneu l’opció Run (també podeu executar el goal de Maven spring-boot:run). Veureu que apareix una finestra a NetBeans amb l’execució de l’aplicació amb Spring (vegeu la figura).

Figura Execució de l’aplicació amb el servei web

Podeu provar que el desplegament ha anat bé consultant el document WSDL generat a l’enllaç localhost:8080/soapws/companyoperations.wsdl amb qualsevol navegador, i heu de veure el document WSDL de definició del servei generat:

  1. <wsdl:definitions targetNamespace="http://cat.xtec.ioc/domain/company/services">
  2. <wsdl:types></wsdl:types>
  3. <wsdl:message name="getCompanyRequest"></wsdl:message>
  4. <wsdl:message name="getCompanyResponse"></wsdl:message>
  5. <wsdl:portType name="CompaniesPort"></wsdl:portType>
  6. <wsdl:binding name="CompaniesPortSoap11" type="tns:CompaniesPort"></wsdl:binding>
  7. <wsdl:service name="CompaniesPortService"></wsdl:service>
  8. </wsdl:definitions>

Prova del servei web

Si tot ha anat bé ja teniu el servei web desplegat i a punt per provar-lo. Però com ho fem? Quin format han de tenir els missatges?

Quan es desplega el 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 fitxer WSDL 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 on hi ha aquestes operacions. Aquesta estructura permet reutilitzar la part abstracta del document.

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>:

  1. <wsdl:types>
  2. <xs:schema elementFormDefault="qualified" targetNamespace="http://cat.xtec.ioc/domain/company/services">
  3. <xs:import namespace="http://cat.xtec.ioc/domain/company" schemaLocation="company.xsd"/>
  4. <xs:element name="getCompanyRequest">
  5. <xs:complexType>
  6. <xs:sequence>
  7. <xs:element name="cif" type="xs:string"/>
  8. </xs:sequence>
  9. </xs:complexType>
  10. </xs:element>
  11. <xs:element name="getCompanyResponse">
  12. <xs:complexType>
  13. <xs:sequence>
  14. <xs:element name="company" type="company:company"/>
  15. </xs:sequence>
  16. </xs:complexType>
  17. </xs:element>
  18. </xs:schema>
  19. </wsdl:types>

Per exemple, si us hi fixeu, defineix que les peticions a l’operació getCompanyRequest reben un paràmetre de tipus string i a les respostes es torna un tipus de dades complex anomenat company que està format per un int i quatre string (cif, name, employees, email i web).

La definició del tipus complex company s’importa d’un fitxer XSD separat que té la definició del model del domini.

  1. <xs:schema elementFormDefault="qualified" targetNamespace="http://cat.xtec.ioc/domain/company">
  2. <xs:complexType name="company">
  3. <xs:sequence>
  4. <xs:element name="cif" type="xs:string"/>
  5. <xs:element name="name" type="xs:string"/>
  6. <xs:element name="employees" type="xs:int"/>
  7. <xs:element name="email" type="xs:string"/>
  8. <xs:element name="web" type="xs:string"/>
  9. </xs:sequence>
  10. </xs:complexType>
  11. </xs:schema>

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. <wsdl:message name="getCompanyRequest">
  2. <wsdl:part element="tns:getCompanyRequest" name="getCompanyRequest">
  3. </wsdl:part>
  4. </wsdl:message>
  5. <wsdl:message name="getCompanyResponse">
  6. <wsdl:part element="tns:getCompanyResponse" name="getCompanyResponse">
  7. </wsdl:part>
  8. </wsdl:message>

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

  1. <wsdl:portType name="CompaniesPort">
  2. <wsdl:operation name="getCompany">
  3. <wsdl:input message="tns:getCompanyRequest" name="getCompanyRequest">
  4. </wsdl:input>
  5. <wsdl:output message="tns:getCompanyResponse" name="getCompanyResponse">
  6. </wsdl:output>
  7. </wsdl:operation>
  8. </wsdl:portType>

Veiem, per exemple, que l’operació getCompanyRequest rep com a paràmetre d’entrada un tns:getCompanyRequest i que retorna un tns:getCompanyResponse. Aquests tipus de dades han estat definides completament en el document XSD que hem creat per definir el servei.

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

  • binding
  • service

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

  1. <wsdl:binding name="CompaniesPortSoap11" type="tns:CompaniesPort">
  2. <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
  3. <wsdl:operation name="getCompany">
  4. <soap:operation soapAction=""/>
  5. <wsdl:input name="getCompanyRequest">
  6. <soap:body use="literal"/>
  7. </wsdl:input>
  8. <wsdl:output name="getCompanyResponse">
  9. <soap:body use="literal"/>
  10. </wsdl:output>
  11. </wsdl:operation>
  12. </wsdl:binding>

L’element <service>, per la seva part, defineix el lloc físic on hi ha el servei web (adreça URL).

  1. <wsdl:service name="CompaniesPortService">
  2. <wsdl:port binding="tns:CompaniesPortSoap11" name="CompaniesPortSoap11">
  3. <soap:address location="http://localhost:8080/soapws"/>
  4. </wsdl:port>
  5. </wsdl:service>

En el nostre cas, el servei web es a l’URL localhost:8080/soapws.

Amb tot això ja sabem el format dels missatges SOAP que cal enviar per accedir al servei web i on cal enviar-los; creeu un fitxer anomenat request.xml, que representarà el missatge SOAP de petició, amb el següent contingut:

  1. <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  2. xmlns:gs="http://cat.xtec.ioc/domain/company/services">
  3. <soapenv:Header/>
  4. <soapenv:Body>
  5. <gs:getCompanyRequest>
  6. <gs:cif>XXXXXXXX</gs:cif>
  7. </gs:getCompanyRequest>
  8. </soapenv:Body>
  9. </soapenv:Envelope>

I ja tan sols ens queda fer una petició al servei web per provar-ho. La nostra proposta és que feu servir cURL, que és una eina molt útil per fer peticions HTTP de diferents tipus i que és multiplataforma. Si feu servir Linux possiblement ja la tingueu instal·lada al sistema, i en cas que utilitzeu Windows la podeu descarregar del següent enllaç: curl.haxx.se/download.html.

La sintaxi de cURL per fer peticions és molt senzilla; per exemple, per consultar les dades de l’empresa que té per CIF XXXXXXXX feu un command prompt:

curl --header "content-type: text/xml" -d @request.xml http://localhost:8080/soapws/

Indiquem que la petició s’envia al fitxer request.xml amb el tipus MIME text/xml. Aquest fitxer és el que acabeu de crear amb el missatge SOAP de petició, i l’URL és l’URL on escolta el servei web.

Si executeu la comanda anterior i tot ha anat correctament, veureu la resposta del servei web amb les dades de l’empresa que té per CIF XXXXXXXX:

  1. <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  2. <SOAP-ENV:Header/>
  3. <SOAP-ENV:Body>
  4. <ns3:getCompanyResponse xmlns:ns2="http://cat.xtec.ioc/domain/company" xmlns:ns3="http://cat.xtec.ioc/domain/company/services">
  5. <ns3:company>
  6. <ns2:cif>XXXXXXXX</ns2:cif>
  7. <ns2:name>Oracle</ns2:name>
  8. <ns2:employees>5000</ns2:employees>
  9. <ns2:email>oracle@oracle.com</ns2:email>
  10. <ns2:web>http://www.oracle.com</ns2:web>
  11. </ns3:company>
  12. </ns3:getCompanyResponse>
  13. </SOAP-ENV:Body>
  14. </SOAP-ENV:Envelope>

I això és tot! Hem vist els passos que us cal fer per desenvolupar, desplegar i provar un servei web SOAP amb Spring-WS seguint una estratègia Contract First.

Fent servir la consulta de dades d’empreses des d'una aplicació Java 'stand-alone'

Crearem una aplicació Java que consumeixi el servei web de consulta de dades d’empreses i ho farem desenvolupant un client Java stand-alone que accedirà al servei web amb Spring-WS.

Els passos generals per fer un client amb Spring-WS són els següents:

  1. Generar els artefactes necessaris per poder consumir el servei web a partir de la definició del servei en format WSDL.
  2. Codificar la classe que farà la crida al servei web heretant de la classe WebServiceGatewaySupport que proporciona Spring-WS.
  3. Compilar i executar el client.

El procediment que expliquem és genèric i serveix per a qualsevol servei web SOAP, estigui o no codificat amb Spring-WS. Només ens cal la definició WSDL del servei.

El client que farem consultarà les dades de les empreses d’un servei web SOAP desenvolupat amb Spring-WS. Descarregueu el codi del servei web que consultarem en el següent

enllaç ( 15.3 KB )
i importeu-lo a NetBeans.

Prerequisits per a l'execució

Abans d’executar la classe cal que us assegureu de tenir el servidor Glassfish parat, ja que, si no, no podrà arrencar el servei Tomcat on desplegarem l’aplicació per un conflicte amb els ports, ja que tant Glassfish com Tomcat estan configurats per utilitzar el port 8080 per defecte.

Quan tingueu el projecte importat a NetBeans cal que desplegueu el servei web que conté; per exemple, creant una aplicació executable que s’executarà amb un contenidor de servlets Tomcat que Spring porta incorporat. Ho podeu fer de diverses maneres, una és fent clic amb el botó dret damunt de la classe Application del paquet cat.xtec.ioc.client i seleccionant Run File (vegeu la figura).

Figura Execució del servei web SOAP de consulta d’empreses

També ho podeu fer executant el goal de Maven spring-boot:run. Amb qualsevol de les dues opcions veureu que apareix una finestra a NetBeans amb l’execució de l’aplicació amb Spring (vegeu la figura).

Figura Finestra d’execució del servei web SOAP de consulta d’empreses

Podeu provar que el desplegament ha anat bé consultant el document WSDL generat a l’enllaç localhost:8080/soapws/companyoperations.wsdl amb qualsevol navegador, i heu de veure el document WSDL de definició del servei.

Després de desplegar el servei web que volem consultar ens cal crear el client Java que el consultarà amb Spring-WS.

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

El primer que cal fer és generar els artefactes necessaris per poder consumir el servei web des d’aquest client a partir de la seva definició en format WSDL. Això es pot fer de diverses formes, i una manera senzilla és que ho faci un plugin de Maven en temps de construcció 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.

Afegiu les següents línies al fitxer pom.xml i feu un Reload POM del projecte:

  1. <plugin>
  2. <groupId>org.jvnet.jaxb2.maven2</groupId>
  3. <artifactId>maven-jaxb2-plugin</artifactId>
  4. <version>0.13.1</version>
  5. <executions>
  6. <execution>
  7. <goals>
  8. <goal>generate</goal>
  9. </goals>
  10. </execution>
  11. </executions>
  12. <configuration>
  13. <schemaLanguage>WSDL</schemaLanguage>
  14. <generatePackage>cat.xtec.ioc.domain.companies.wsdl</generatePackage>
  15. <schemas>
  16. <schema>
  17. <url>http://localhost:8080/soapws/companyoperations.wsdl</url>
  18. </schema>
  19. </schemas>
  20. </configuration>
  21. </plugin>

Aquest plugin generarà automàticament les classes necessàries per consultar el servei web de consulta de dades d’empreses en temps de compilació i les deixarà al paquet cat.xtec.ioc.domain.companies.wsdl. Fixeu-vos també que estem especificant en la configuració del plugin la localització del document WSDL amb la definició del servei.

Per tal que el projecte funcioni cal que l’executeu amb JDK 1.8; podeu canviar el JDK a les propietats del projecte, a l’apartat Build / Compile / Java Plattform .

Si feu clic amb el botó dret damunt el projecte “Springsoapempclientioc” i feu Clean and Build veureu que el plugin genera, entre d’altres, les següents classes a partir del document WSDL de definició de servei:

  • Company.java
  • GetCompanyRequest.java
  • GetCompanyResponse.java

Un cop generats els artefactes necessaris cal que creeu la classe Java que farà la crida al servei web. Creeu-la, per exemple, al paquet cat.xtec.ioc.client i anomeneu-la CompaniesClient:

  1. package cat.xtec.ioc.client;
  2.  
  3. import cat.xtec.ioc.domain.companies.wsdl.GetCompanyRequest;
  4. import cat.xtec.ioc.domain.companies.wsdl.GetCompanyResponse;
  5. import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
  6. import org.springframework.ws.soap.client.core.SoapActionCallback;
  7.  
  8. public class CompaniesClient extends WebServiceGatewaySupport {
  9.  
  10. public GetCompanyResponse getCompanyInformation(String cif) {
  11. GetCompanyRequest request = new GetCompanyRequest();
  12. request.setCif(cif);
  13. GetCompanyResponse response = (GetCompanyResponse) getWebServiceTemplate()
  14. .marshalSendAndReceive(request,
  15. new SoapActionCallback("http://localhost:8080/soapws/getCompanyResponse"));
  16.  
  17. return response;
  18. }
  19.  
  20. }

Aquesta classe hereta d’una classe que proporciona Spring anomenada WebServiceGatewaySupport i utilitza les classes generades per fer una crida al servei web mitjançant un template que proporciona la seva classe base.

També ens cal crear una classe de configuració per indicar a Spring que faci servir JAXB per fer les transformacions de missatge SOAP a objecte Java, i a la inversa. Creeu aquesta classe Java també al paquet cat.xtec.ioc.client i anomeneu-la ClientAppConfig:

  1. package cat.xtec.ioc.client;
  2.  
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.oxm.jaxb.Jaxb2Marshaller;
  6.  
  7. @Configuration
  8. public class ClientAppConfig {
  9.  
  10. @Bean
  11. public Jaxb2Marshaller marshaller() {
  12. Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
  13. marshaller.setContextPath("cat.xtec.ioc.domain.companies.wsdl");
  14. return marshaller;
  15. }
  16.  
  17. @Bean
  18. public CompaniesClient companiesClient(Jaxb2Marshaller marshaller) {
  19. CompaniesClient client = new CompaniesClient();
  20. client.setDefaultUri("http://localhost:8080/soapws/companies.wsdl");
  21. client.setMarshaller(marshaller);
  22. client.setUnmarshaller(marshaller);
  23. return client;
  24. }
  25. }

Finalment, creeu una classe Java, també al paquet cat.xtec.ioc.client, i l’anomeneu RunCompaniesClient, amb el següent codi:

  1. package cat.xtec.ioc.client;
  2.  
  3. import cat.xtec.ioc.domain.companies.wsdl.GetCompanyResponse;
  4. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  5.  
  6. public class RunCompaniesClient {
  7.  
  8. public static void main(String[] args) {
  9. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  10. ctx.register(ClientAppConfig.class);
  11. ctx.refresh();
  12. CompaniesClient companiesClient = ctx.getBean(CompaniesClient.class);
  13. System.out.println("For Company XXXXXXXX");
  14. GetCompanyResponse response = companiesClient.getCompanyInformation("XXXXXXXX");
  15. System.out.println("CIF: " + response.getCompany().getCif());
  16. System.out.println("Name: " + response.getCompany().getName());
  17. System.out.println("Num. employees: " + response.getCompany().getEmployees());
  18. System.out.println("Email: " + response.getCompany().getEmail());
  19. System.out.println("Web: " + response.getCompany().getWeb());
  20. }
  21. }

Aquesta classe té un mètode main que s’encarrega de crear un objecte de tipus CompaniesClient amb la configuració especificada a ClientAppConfig, fer una petició al servei web de consulta de dades d’empreses per recuperar les dades de l’empresa que té el CIF XXXXXXXX amb aquesta classe i mostrar els resultats per a la sortida estàndard.

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

Figura Execució del client del servei web SOAP de consulta d’empreses

I obtindreu per consola les dades de l’empresa que té el CIF XXXXXXXX:

CIF: XXXXXXXX
Name: Oracle
Num. employees: 5000
Email: oracle@oracle.com
Web: http://www.oracle.com

Què s'ha après?

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

Concretament, heu après:

  • Les nocions bàsiques dels serveis web SOAP amb Spring-WS.
  • La diferència entre Contract First i Code First.
  • Les diferències bàsiques entre JAX-WS i Spring-WS.
  • Desenvolupar, desplegar i provar un servei web SOAP amb Spring-WS.
  • Desenvolupar i provar un client Java que consulti un servei web SOAP mitjançant Spring-WS.
Anar a la pàgina anterior:
Referències
Anar a la pàgina següent:
Exercicis d'autoavaluació