Un webservice es un servicio a través de internet, una interfaz mediante la que dos máquinas o aplicaciones se comunican entre sí, usando el protocolo HTTP o HTTPS.
Este recurso se caracteriza por ser multiplataforma (cliente y servidor no tienen por qué usar la misma tecnología para comunicarse), y distribuido (está disponible para múltiples clientes).
Cuando se utiliza un webservice un cliente manda una solicitud al servidor, y luego se desencadena una acción que genera una respuesta por parte de este.
Existen 2 tipos de webservice que se diferencian por la forma de comunicación, por un lado, el más antiguo SOAP, que transmite comunicaciones a través de XML, mientras que REST es mucho más ligero y moderno, transmite las comunicaciones a través de archivos JSON, lo cual es muy ventajoso de usar con Javascript.
Existen distintos códigos de respuesta http en un webservice:
- Respuestas informativas (100 a 199)
- Respuestas satisfactorias (200 a 299)
- Redirecciones (300-399)
- Errores en el cliente (400 – 499)
- Errores en el servidor (500 – 599)
Dentro de esos rangos de respuesta, las más comunes que debemos conocer son:
200 OK :La solicitud ha tenido éxito.
403 Forbidden : El cliente no posee permisos requeridos para el contenido solicitado por lo que el servidor rechaza la request.
404 Not Found : El servidor no pudo encontrar el contenido solicitado, código de respuesta muy famoso (típico cuando queremos ingresar a una web inexistente).
500 Internal Server Error : Fallo en el servidor que el mismo no supo como manejar, como ser errores de programación, errores en formularios, mala configuración en fichero .htaccess, etc.
503 Service Unavailable : El servicio no está disponible, es decir que el servidor no puede manejar la petición o porque está sobrecargado o está en mantenimiento.
Para ver un listado completo de todos los tipos de respuesta http, podemos ingresar en este link de Mozilla.
Qué es un WSDL
Un WSDL es un fichero XML que contiene una notación para describir un servicio web, el WSDL indica al cliente como componer una solicitud y describe la interfaz que proporciona el proveedor del servicio web.
A continuación, les dejo un ejemplo de WSDL:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="Calculator" targetNamespace="http://ws/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://ws/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<wsdl:message name="multiplicacion">
<wsdl:part name="arg0" type="xsd:int">
</wsdl:part>
<wsdl:part name="arg1" type="xsd:int">
</wsdl:part>
</wsdl:message>
<wsdl:message name="multiplicacionResponse">
<wsdl:part name="return" type="xsd:int">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="Calculator">
<wsdl:operation name="multiplicacion">
<wsdl:input name="multiplicacion" message="tns:multiplicacion">
</wsdl:input>
<wsdl:output name="multiplicacionResponse" message="tns:multiplicacionResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="CalculatorWsSecuritySoapBinding" type="tns:Calculator">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="multiplicacion">
<soap:operation soapAction="" style="rpc"/>
<wsdl:input name="multiplicacion">
<soap:body use="literal" namespace="http://ws/"/>
</wsdl:input>
<wsdl:output name="multiplicacionResponse">
<soap:body use="literal" namespace="http://ws/"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="Calculator">
<wsdl:port name="calculator" binding="tns:CalculatorWsSecuritySoapBinding">
<soap:address location="http://192.168.10.2:8087//services/ws/Calculator"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
El fichero WSDL es necesario para comunicarse con el webservice desde el cliente o desde algún software de testing como ser SoapUI (enlace aquí) o Postman (enlace aquí).
Al crear un proyecto SOAP en SoapUI ingresamos el WSDL para comenzar a hacer requests.

La url a un WSDL puede tener el siguiente formato:
http://10.97.68.53:8087/webServices-1.0/CalculatorWsSecurity/CalculatorWsSecurity?wsdl
Al generar el proyecto SOAP, si el web service está bien configurado, automáticamente nos reconoce las operaciones que permite el mismo y nos genera un request de prueba, en el cual solo debemos poner los argumentos y al presionar el botón “play” verde obtenemos el retorno desde el servidor.

Implementación básica de webservice SOAP (servidor)
Repositorio: https://github.com/nbent1996/ws-ejb.git
Servidor de aplicaciones
Para publicar este webservice necesitamos un servidor de aplicaciones, como lo pueden ser Wildfly, Glassfish, Tomcat, etc. En este post te dejo un ambiente de pruebas con Wildfly y Docker que se genera con solo ejecutar un script (cumpliendo con algunos requisitos previos).
Código generado
Para implementar un webservice desde java será necesario codificar 2 clases, una de implementación y una interfaz. En este ejemplo se implementará el webservice como EJB Stateless (JAXWS_EJB3), y luego será empaquetado en un .JAR.
Para ejemplificar como es la sintaxis de esta implementación, se desarrolló una calculadora básica, la cual contiene las clases Calculator y ICalculator.
Primero que nada, generamos el proyecto con estas características en IntelliJ:

Ahora debemos agregarle al proyecto el soporte para el framework Maven.

Esto nos facilitará la compilación del JAR y la generación del WSDL.
Para poder usar este proyecto sin inconvenientes, será necesario configurar las siguientes dependencias en Maven:
<properties>
<cxf.version>3.5.3</cxf.version>
</properties>
<dependencies>
<dependency>
<groupId>org.wildfly.bom</groupId>
<artifactId>wildfly-jakartaee8-with-tools</artifactId>
<version>26.1.1.Final</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Import the Common Annotations API (JSR-250), we use provided scope
as the API is included in JBoss EAP -->
<dependency>
<groupId>org.jboss.spec.javax.annotation</groupId>
<artifactId>jboss-annotations-api_1.3_spec</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!-- Import the EJB API, we use provided scope as the API is included in JBoss EAP -->
<dependency>
<groupId>org.jboss.spec.javax.ejb</groupId>
<artifactId>jboss-ejb-api_3.2_spec</artifactId>
<version>2.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.xml.ws</groupId>
<artifactId>jboss-jaxws-api_2.3_spec</artifactId>
<version>2.0.0.Final</version>
</dependency>
<dependency>
<groupId>javax.jws</groupId>
<artifactId>jsr181-api</artifactId>
<version>1.0-MR1</version>
</dependency>
</dependencies>
Entonces ya podemos generar en un paquete el webservice.
Clase Calculator.java:
@Stateless
@WebService(serviceName="Calculator")
@SOAPBinding(style=SOAPBinding.Style.RPC)
@Remote(ICalculator.class)
public class Calculator implements ICalculator {
@WebMethod(operationName="multiplicacion")
@Override
public int multiplicacion(@WebParam(name="arg0")int a, @WebParam(name="arg1") int b){
return a*b;
}
}
Interfaz ICalculator.java:
@Remote
public interface ICalculator {
int multiplicacion(int a , int b);
}
Si observamos, al código que usualmente escribimos para una clase, método y parámetros se agregan unas anotaciones que son necesarias para poder generar luego el WSDL.
Como generar el WSDL para el servicio con Maven y Apache CXF
Apache CXF es una amplia librería de código abierto orientada a servicios web, que tiene bastantes especificaciones como ser SOAP y WS Security.
Usando la librería Apache CXF podemos configurar Maven en nuestro proyecto java para que use dicha librería y genere automáticamente el WSDL en cada compilación.
Para lograr esto debemos agregar la etiqueta build con la siguiente configuración:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-java2ws-plugin</artifactId>
<version>${cxf.version}</version>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-simple</artifactId>
<version>${cxf.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>process-classes</id>
<phase>process-classes</phase>
<configuration>
<outputFile>${project.build.outputDirectory}/Calculator.wsdl</outputFile>
<className>ws.Calculator</className>
<address>http://192.168.10.2:8087//services/ws/Calculator</address>
<portName>calculator</portName>
<soap12>false</soap12>
<serviceName>Calculator</serviceName>
<verbose>true</verbose>
<genWsdl>true</genWsdl>
</configuration>
<goals>
<goal>java2ws</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
En la misma configuramos el JDK a usar, y el plugin de Apache CXF denominado cxf-java2ws-plugin (usado para pasar de código java a WSDL) el cual requiere algunas dependencias, y dentro del tag “execution” tenemos algunos parámetros que debemos configurar con cuidado para que el WSDL sea generado correctamente.
Address es la url al endpoint, classname es la clase que implementa la interfaz del webservice y debemos observar los nombres que ponemos a portName y serviceName porque esos parámetros luego serán usados en el cliente que consuma este Webservice.
Una vez tenemos todo en orden, ejecutamos el comando:
mvn clean install
Y se nos compila el Jar con el WSDL correspondiente.
Esto puede ser desplegado en Wildfly a través de la consola de admin de uno de los 4 nodos de prueba generados con el testingEnvironment (https://github.com/nbent1996/testingEnvironmentBlog).
Implementación básica de webservice SOAP (cliente)
Repositorio: https://github.com/nbent1996/ws-client
El siguiente snippet nos será de ayuda para implementar un cliente java consola para pruebas de un determinado Web Service
private static void consumirWebService() {
try {
String endpointUrl = "http://localhost:8087/webServices-1.0/Calculator/Calculator?wsdl";
QName serviceName = new QName("http://ws/",
"Calculator");
QName portName = new QName("http://ws/",
"calculator");
/** Create a service and add at least one port to it. **/
Service service;
service = Service.create(serviceName);
service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, endpointUrl);
/** Create a Dispatch instance from a service.**/
Dispatch<SOAPMessage> dispatch = service.createDispatch(portName,
SOAPMessage.class, Service.Mode.MESSAGE);
/** Create SOAPMessage request. **/
// compose a request message
MessageFactory mf = null;
mf = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
// Create a message. This example works with the SOAPPART.
SOAPMessage request = mf.createMessage();
SOAPPart part = request.getSOAPPart();
// Obtain the SOAPEnvelope and header and body elements.
SOAPEnvelope env = part.getEnvelope();
SOAPHeader header = env.getHeader();
SOAPBody body = env.getBody();
// Construct the message payload.
SOAPElement operation = body.addChildElement("multiplicacion", "ns1",
"http://ws/");
SOAPElement value1 = operation.addChildElement("arg0");
SOAPElement value2 = operation.addChildElement("arg1");
value1.addTextNode("25");
value2.addTextNode("25");
request.saveChanges();
/** Invoke the service endpoint. **/
SOAPMessage response = dispatch.invoke(request);
/** Process the response. **/
SOAPBody bodyFinal = response.getSOAPBody();
SOAPElement elemento = getFirstBodyElement(bodyFinal);
String resultado = elemento.getTextContent();
System.out.println("Resultado de multiplicar arg0 x arg1 (25x25) = " + resultado);
} catch (SOAPException e) {
e.printStackTrace();
}
}
public static SOAPElement getFirstBodyElement(SOAPBody body) {
for (Iterator<?> iterator = body.getChildElements(); iterator.hasNext(); ) {
Object child = iterator.next();
if (child instanceof SOAPElement) {
return (SOAPElement) child;
}
}
return null;
}