Laboratorio de Simple Object Access Protocol (SOAP)
Objetivo
Vamos a crear un cliente en Spring Boot que va a permitir consumir un servicio web creado en SOAP en la llamado Calculator, podemos encontrar las especificaciones en el siguiente enlace http://www.dneonline.com/calculator.asmx.
Materiales
Para este laboratorio necesitas tener instalado en tu computador:
- Spring Initializr
- Open JDK 17
- Apache Maven 3.9.7
- Spring Boot 2.6.3
- IntelliJ IDEA
- Git
Conceptos básicos.
SOAP
SOAP (Simple Object Access Protocol) es un protocolo de comunicación que permite la comunicación entre aplicaciones de software. Está basado en XML, define una estructura de mensajes que pueden ser intercambiados entre aplicaciones.
WSDL
WSDL (Web Services Description Language) es un lenguaje basado en XML que se utiliza para describir servicios web. Un archivo WSDL describe los métodos que un servicio web expone, los parámetros que recibe y los tipos de datos que retorna.
Spring Boot
Spring Boot es un framework de Java que facilita la creación de aplicaciones web. Proporciona un conjunto de herramientas y librerías que permiten crear aplicaciones web de forma rápida y sencilla.
Maven
Maven es una herramienta de gestión de proyectos que se utiliza para compilar, empaquetar y desplegar aplicaciones Java. Permite gestionar las dependencias de un proyecto y automatizar tareas de construcción.
JAXB
JAXB (Java Architecture for XML Binding) es una tecnología de Java que se utiliza para convertir objetos de Java a XML y viceversa. Permite mapear objetos de Java a documentos XML y viceversa.
Marshalling
Marshalling es el proceso de convertir un objeto de Java a un documento XML. JAXB se encarga de convertir los objetos de Java a XML.
Unmarshalling
Unmarshalling es el proceso de convertir un documento XML a un objeto de Java. JAXB se encarga de convertir los documentos XML a objetos de Java.
Introducción
En este laboratorio vamos a crear un cliente en Spring Boot que va a permitir consumir un servicio web creado en SOAP. El servicio web que vamos a consumir se llama Calculator y podemos encontrar las especificaciones en el siguiente enlace http://www.dneonline.com/calculator.asmx.
El servicio web Calculator tiene los siguientes métodos:
Add: Suma dos números enteros y retorna el resultado.
Subtract: Resta dos números enteros y retorna el resultado.
Multiply: Multiplica dos números enteros y retorna el resultado.
Divide: Divide dos números enteros y retorna el resultado.
Vamos a crear un cliente en Spring Boot que va a permitir consumir estos métodos del servicio web Calculator.
Instrucciones
Vamos a empezar creando nuestro proyecto con Spring Initializr, para ello vamos a seguir los siguientes pasos:
Abre Spring Initializr en tu navegador web https://start.spring.io/.
Llena los campos del formulario con la siguiente información:
Project: Maven
Language: Java
Spring Boot: 3.3.0
Group: com.soap
Artifact: SpringBootSoap
Name: SpringBootSoap
Description: Spring Boot Soap Client
Package name: com.soap
Packaging: Jar
Java: 17
Haz clic en el botón Generate para descargar el proyecto.
Descomprime el archivo descargado y ábrelo en IntelliJ IDEA.
Crear un cliente SOAP
Vamos a crear un cliente SOAP que consuma el servicio web Calculator. Para ello vamos a seguir los siguientes pasos:
- Abrimos el proyecto descomprimido en IntelliJ IDEA.
El proyecto que vamos a consumir desde el cliente está en la versión de Spring Boot 2.7.10, actualmente no nos permite Spring Initializr crear un proyecto con esta versión, asi que realizamos este cambio en el archivo pom.xml del proyecto que acabamos de crear.
Si deseas puedes crear un banner personalizado para tu aplicación, para ello puedes seguir los pasos en el siguiente enlace https://devops.datenkollektiv.de/banner.txt/index.html escribes el nombre de tu aplicación y copias el banner generado en el archivo banner.txt que se encuentra en la carpeta resources.
- Corremos nuestra aplicación para comprobar que todo está funcionando correctamente.
- Ahora vamos a configurar nuestro archivo pom.xml para agregar un plugin que nos permita generar las clases necesarias para consumir el servicio web Calculator.
Si todo salio bien, comparemos si nuestro archivo pom.xml quedo de la siguiente manera:
<?xml version="1.0" encoding="UTF-8"?>
project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
< xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
<relativePath/> <!-- lookup parent from repository -->
<parent>
</groupId>com.soap</groupId>
<artifactId>SpringBootSoap</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBootSoap</name>
<description>Spring Boot Soap Client</description>
<properties>
<java.version>17</java.version>
<properties>
</dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
<dependency>
</
dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
<dependency>
</dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<dependency>
</dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<dependency>
</dependencies>
</
build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<plugin>
</plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.14.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
<goals>
</execution>
</executions>
</configuration>
<schemaLanguage>WSDL</schemaLanguage>
<generateDirectory>${project.basedir}/src/main/java</generateDirectory>
<generatePackage>com.soap.wsdl</generatePackage>
<schemas>
<schema>
<url>http://www.dneonline.com/calculator.asmx?WSDL</url>
<schema>
</schemas>
</configuration>
</plugin>
</plugins>
</build>
</
project> </
En algunas ocasiones posiblemente nos entreguen en físico el archivo calculator.wsdl que contiene la descripción del servicio web, en este caso debemos agregarlo en la carpeta resources del proyecto, podemos crear una carpeta llamada wsdl y agregar el archivo calculator.wsdl. Luego debemos modificar el archivo pom.xml para que tome este archivo y genere las clases necesarias.
La sección de configuración cambiaría de la siguiente manera:
configuration>
<schemaLanguage>WSDL</schemaLanguage>
<schemaDirectory>${project.basedir}/src/main/resources/wsdl</schemaDirectory>
<schemas>
<schemaFiles>CalculatorApi.wsdl</schemaFiles>
<packageName>com.soap.wsdl</packageName>
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<schemas>
</configuration> </
- Vamos a limpiar nuestro proyecto con el siguiente comando:
mvn clean
- Ahora vamos a compilar nuestro proyecto con el siguiente comando:
mvn compile
- Si todo salió bien, podemos ver que se generaron las clases necesarias para consumir el servicio web Calculator.
- Ahora vamos a crear un paquete llamado com.soap.client y dentro de este paquete vamos a crear una clase llamada SoapClient.
En esta clase vamos a agregar el código necesario para consumir el servicio web Calculator.
Empezamos con el método AddResponse que se encarga de sumar dos números.
package com.soap.client;
import com.soap.wsdl.*;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.springframework.ws.soap.client.core.SoapActionCallback;
public class SoapClient extends WebServiceGatewaySupport {
/**
* Metodo que se encarga de sumar dos numeros
*
* @param numberA
* @param numberB
* @return AddResponse
*/
1public AddResponse getAddResponse(int numberA, int numberB) {
2= new Add();
Add addRequest 3.setIntA(numberA);
addRequest4.setIntB(numberB);
addRequest
5= new SoapActionCallback("http://tempuri.org/Add");
SoapActionCallback soapActionCallback
6= (AddResponse) getWebServiceTemplate().marshalSendAndReceive("http://www.dneonline.com/calculator.asmx", addRequest, soapActionCallback);
AddResponse addResponse
7return addResponse;
}
}
- 1
- Creamos el método getAddResponse que recibe dos números enteros y retorna un objeto de tipo AddResponse.
- 2
- Creamos un objeto de tipo Add que representa la petición de sumar dos números.
- 3
- Seteamos el primer número en la petición.
- 4
- Seteamos el segundo número en la petición.
- 5
- Creamos un objeto de tipo SoapActionCallback que representa la acción SOAP que se va a realizar.
- 6
- Realizamos la petición al servicio web y obtenemos la respuesta.
- 7
- Retornamos la respuesta.
Ahora vamos a crear el paquete com.soap.config y dentro de este paquete vamos a crear una clase llamada SoapConfig.
Dentro de esta clase vamos a agregar la configuración necesaria para consumir el servicio web Calculator.
package com.soap.config;
import com.soap.client.SoapClient;
1import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
2import org.springframework.oxm.jaxb.Jaxb2Marshaller;
@Configuration
public class SoapConfig {
3@Bean
4public Jaxb2Marshaller marshaller(){
5= new Jaxb2Marshaller();
Jaxb2Marshaller marshaller 6.setContextPath("com.soap.wsdl");
marshaller7return marshaller;
}
8@Bean
9public SoapClient getSoapClient(Jaxb2Marshaller marshaller){
10= new SoapClient();
SoapClient soapClient 11.setDefaultUri("http://www.dneonline.com/calculator.asmx");
soapClient12.setMarshaller(marshaller);
soapClient13.setUnmarshaller(marshaller);
soapClient
14return soapClient;
}
}
- 1
- Importamos las clases necesarias.
- 2
- Importamos la clase Jaxb2Marshaller que se encarga de convertir los objetos de Java a XML y viceversa.
- 3
- Anotamos la clase con (Configuration?) para indicar que es una clase de configuración.
- 4
- Creamos un bean de tipo Jaxb2Marshaller.
- 5
- Creamos un objeto de tipo Jaxb2Marshaller.
- 6
- Seteamos el contexto del paquete donde se encuentran las clases generadas a partir del archivo WSDL.
- 7
- Retornamos el objeto Jaxb2Marshaller.
- 8
- Creamos un bean de tipo SoapClient.
- 9
- Creamos un método que recibe un objeto de tipo Jaxb2Marshaller y retorna un objeto de tipo SoapClient.
- 10
- Creamos un objeto de tipo SoapClient.
- 11
- Seteamos la URL del servicio web.
- 12
- Seteamos el marshaller en el objeto SoapClient.
- 13
- Seteamos el unmarshaller en el objeto SoapClient.
- 14
- Retornamos el objeto SoapClient.
En esta clase creamos un bean de tipo Jaxb2Marshaller que se encarga de convertir los objetos de Java a XML y viceversa.
También creamos un bean de tipo SoapClient que se encarga de consumir el servicio web Calculator.
¿Qué es Unmarchalling y Marchalling?
Marshalling es un concepto que se utiliza en la serialización de objetos, es decir, convertir un objeto en un formato que se pueda almacenar o transmitir. Unmarshaller es el proceso inverso, es decir, convertir un objeto en un formato que se pueda utilizar en la aplicación. Recordemos que SOAP trabaja con archivos XML.
Tambien es importante conocer que cuando trabajamos con servicios REST quien utiliza los archivos JSON, en este caso se utiliza el concepto de Deserialización y Serialización.
Puedes encontrar más información en el siguiente enlace https://www.baeldung.com/jaxb.
Para probar nuestra aplicación y verificar que todo está resultando bien vamos al archivo SpringBootSoapApplication y agregamos el siguiente código:
package com.soap;
import com.soap.client.SoapClient;
import com.soap.wsdl.AddResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringBootSoapApplication {
1private static final Logger LOGGER = LoggerFactory.getLogger(SpringBootSoapApplication.class);
public static void main(String[] args) {
.run(SpringBootSoapApplication.class, args);
SpringApplication}
2@Bean
3init(SoapClient soapClient){
CommandLineRunner 4return args -> {
5= soapClient.getAddResponse(2, 2);
AddResponse addResponse
6.info("El resultado de la suma de los numeros {} y {} es: {}", 2, 2, addResponse.getAddResult());
LOGGER};
}
}
- 1
- Creamos un objeto de tipo Logger para imprimir mensajes en la consola.
- 2
- Creamos un bean de tipo CommandLineRunner.
- 3
- Creamos un método que recibe un objeto de tipo SoapClient y retorna un objeto de tipo CommandLineRunner.
- 4
- Creamos un objeto de tipo CommandLineRunner.
- 5
- Realizamos una petición al servicio web para sumar dos números.
- 6
- Imprimimos el resultado de la suma en la consola.
En nuestra aplicación principal SpringBootSoapApplication agregamos un bean de tipo CommandLineRunner que se encarga de ejecutar el método init al iniciar la aplicación.
Este método se encarga de consumir el servicio web Calculator y sumar dos números.
Ejecutar la aplicación
Ahora vamos a ejecutar nuestra aplicación para comprobar que todo está funcionando correctamente.
- Corremos nuestra aplicación.
- Verificamos que el resultado de la suma sea correcto.
En la línea de comandos debemos ver el siguiente mensaje:
El resultado de la suma de los numeros 2 y 2 es: 4
Para desarrollar los demás métodos es necesario comentar las líneas que acabamos de ingresar en el archivo SpringBootSoapApplication y agregar el siguiente código:
package com.soap;
import com.soap.client.SoapClient;
import com.soap.wsdl.AddResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringBootSoapApplication {
// private static final Logger LOGGER = LoggerFactory.getLogger(SpringBootSoapApplication.class);
public static void main(String[] args) {
.run(SpringBootSoapApplication.class, args);
SpringApplication}
// @Bean
// CommandLineRunner init(SoapClient soapClient){
// return args -> {
//
// AddResponse addResponse = soapClient.getAddResponse(2, 2);
//
// LOGGER.info("El resultado de la suma de los numeros {} y {} es: {}", 2, 2, addResponse.getAddResult());
// };
// }
}
Ahora vamos a el metodo para restar dos números, para ello vamos a modificar la clase SoapClient.
/**
* Metodo que encarga de restar dos numero
* @param numberA
* @param numberB
* @return SubtractResponse
*/
public SubtractResponse getSubtractResponse(int numberA, int numberB) {
1= new Subtract();
Subtract subtractRequest 2.setIntA(numberA);
subtractRequest3.setIntB(numberB);
subtractRequest
4= new SoapActionCallback("http://tempuri.org/Subtract");
SoapActionCallback soapActionCallback
5= (SubtractResponse) getWebServiceTemplate().marshalSendAndReceive("http://www.dneonline.com/calculator.asmx", subtractRequest, soapActionCallback);
SubtractResponse subtractResponse
6return subtractResponse;
}
- 1
- Creamos un objeto de tipo Subtract que representa la petición de restar dos números.
- 2
- Seteamos el primer número en la petición.
- 3
- Seteamos el segundo número en la petición.
- 4
- Creamos un objeto de tipo SoapActionCallback que representa la acción SOAP que se va a realizar.
- 5
- Realizamos la petición al servicio web y obtenemos la respuesta.
- 6
- Retornamos la respuesta.
Crear el Controlador
Vamos a crear un controlador que se encargue de recibir las peticiones y llamar a los métodos correspondientes en el cliente SOAP.
- Creamos un paquete llamado com.soap.controller y dentro de este paquete vamos a crear una clase llamada SoapController.
En esta clase vamos a agregar el código necesario para crear un controlador que se encargue de recibir las peticiones y llamar a los métodos correspondientes en el cliente SOAP.
package com.soap.controller;
import com.soap.client.SoapClient;
import com.soap.wsdl.AddResponse;
import com.soap.wsdl.SubtractResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class SoapController {
@Autowired
private SoapClient soapClient;
1@PostMapping("/sumar")
2public ResponseEntity<?> add(@RequestParam int numberA, @RequestParam int numberB) {
3= soapClient.getAddResponse(numberA, numberB);
AddResponse addResponse
4Map<String, Integer> response = new HashMap<>();
5.put("resultado", addResponse.getAddResult());
response6return ResponseEntity.ok().body(response);
}
7@PostMapping("/restar")
8public ResponseEntity<?> subtract(@RequestParam int numberA, @RequestParam int numberB) {
9= soapClient.getSubtractResponse(numberA, numberB);
SubtractResponse subtractResponse
10Map<String, Integer> response = new HashMap<>();
11.put("resultado", subtractResponse.getSubtractResult());
response12return ResponseEntity.ok().body(response);
}
}
- 1
- Creamos un endpoint que recibe dos números y retorna la suma de los mismos.
- 2
- Creamos un método que recibe dos números y retorna un objeto de tipo ResponseEntity.
- 3
- Realizamos una petición al servicio web para sumar dos números.
- 4
- Creamos un objeto de tipo Map para almacenar la respuesta.
- 5
- Agregamos el resultado de la suma al objeto Map.
- 6
- Retornamos un objeto de tipo ResponseEntity con el resultado de la suma.
- 7
- Creamos un endpoint que recibe dos números y retorna la resta de los mismos.
- 8
- Creamos un método que recibe dos números y retorna un objeto de tipo ResponseEntity.
- 9
- Realizamos una petición al servicio web para restar dos números.
- 10
- Creamos un objeto de tipo Map para almacenar la respuesta.
- 11
- Agregamos el resultado de la resta al objeto Map.
- 12
- Retornamos un objeto de tipo ResponseEntity con el resultado de la resta.
En esta clase creamos un controlador que se encarga de recibir las peticiones y llamar a los métodos correspondientes en el cliente SOAP.
Ejecutar la aplicación
Ahora vamos a ejecutar nuestra aplicación para comprobar que todo está funcionando correctamente.
- Corremos nuestra aplicación.
Podemos ver que nuestra aplicación se está ejecutando correctamente en el puerto 8080.
Para probar los endpoints que creamos vamos a utilizar Thunder Client, una extensión de Visual Studio Code que nos permite hacer peticiones HTTP.
- Abrimos Visual Studio Code y buscamos la extensión Thunder Client.
Probar el endpoint de suma
Probar el endpoint de resta
Como podemos observar, nuestra aplicación está funcionando correctamente y podemos consumir el servicio web Calculator.
Reto
Crea un método en el cliente SOAP que se encargue de multiplicar dos números.
Crea un endpoint en el controlador que reciba dos números y retorne el resultado de la multiplicación.
Ejecuta la aplicación y prueba el endpoint de multiplicación.
Ver solución
/**
* Metodo que encarga de multiplicar dos numero
* @param numberA
* @param numberB
* @return MultiplyResponse
*/
1public MultiplyResponse getMultiplyResponse(int numberA, int numberB) {
2= new Multiply();
Multiply multiplyRequest 3.setIntA(numberA);
multiplyRequest4.setIntB(numberB);
multiplyRequest
5= new SoapActionCallback("http://tempuri.org/Multiply");
SoapActionCallback soapActionCallback
6return (MultiplyResponse) getWebServiceTemplate().marshalSendAndReceive("http://www.dneonline.com/calculator.asmx", multiplyRequest, soapActionCallback);
}
}
- 1
- Creamos el método getMultiplyResponse que recibe dos números enteros y retorna un objeto de tipo MultiplyResponse.
- 2
- Creamos un objeto de tipo Multiply que representa la petición de multiplicar dos números.
- 3
- Seteamos el primer número en la petición.
- 4
- Seteamos el segundo número en la petición.
- 5
- Creamos un objeto de tipo SoapActionCallback que representa la acción SOAP que se va a realizar.
- 6
- Realizamos la petición al servicio web y obtenemos la respuesta.
1@PostMapping("/multiplicar")
2public ResponseEntity<?> multiply(@RequestParam int numberA, @RequestParam int numberB) {
3= soapClient.getMultiplyResponse(numberA, numberB);
MultiplyResponse multiplyResponse
4Map<String, Integer> response = new HashMap<>();
5.put("resultado", multiplyResponse.getMultiplyResult());
response6return ResponseEntity.ok().body(response);
}
}
- 1
- Creamos un endpoint que recibe dos números y retorna el resultado de la multiplicación.
- 2
- Creamos un método que recibe dos números y retorna un objeto de tipo ResponseEntity.
- 3
- Realizamos una petición al servicio web para multiplicar dos números.
- 4
- Creamos un objeto de tipo Map para almacenar la respuesta.
- 5
- Agregamos el resultado de la multiplicación al objeto Map.
- 6
- Retornamos un objeto de tipo ResponseEntity con el resultado de la multiplicación.
Referencias
:::