Creación de WebService Cliente SOAP mediante WsImport
El objetivo de hoy será revisar los pasos necesarios para realizar la implementación, mediante anotaciones JAX-WS, de la parte cliente de un webservice SOAP. Recordemos que, cuando hablamos de webservices, estamos hablando tanto del servicio publicado en la parte del servidor como del servicio que lo consume desde la parte del cliente. Y, por supuesto, hay que decir tan importante es uno como el otro.
Como ya sabemos, hace algunas semanas estuvimos viendo cómo se creaba el servicio del proyecto servidor, así que hoy usaremos ese ejemplo para completar el ejercicio (ver post Creación de Webservice SOAP mediante Anotaciones). Como dicha implementación quedó correctamente publicada, hoy usaremos su fichero WSDL para desarrollar el servicio del proyecto cliente. El procedimiento que veremos constituye un proceso relativamente estándar.
Creación de Webservice Cliente SOAP
Teniendo en cuenta que ya tenemos creado un webservice publicado y listo para consumir, estos serían los pasos para la creación del servicio cliente.
1º) En primer lugar, tenemos que recuperar la ubicación del fichero WSDL del servicio publicado al que queremos acceder. En nuestro ejemplo, recordemos que la ruta era la siguiente:
http://localhost:8080/AnotacionSoapApp/ws/hola?wsdl
2º) Con esa información, vamos a lanzar la utilidad WSIMPORT. A partir del WSDL, esta utilidad es capaz de generar las clases necesarias para poder invocar al webservice desde cualquier otro proyecto que actúe como cliente.
Para poder ejecutarla, tendremos que ubicarnos en la carpeta "bin" del JDK que tengamos instalado en nuestro equipo. En mi caso, se trata de este directorio:
C:\Program Files\Java\jdk1.8.0_191\bin
3º) Abrimos la línea de comandos de Windows con "cmd". Por si acaso, os recuerdo que tendréis que abrirla en modo Administrador. Nos situamos en la carpeta "bin" del JDK.
cd C:\Program Files\Java\jdk1.8.0_191\bin
Generación de clases mediante WSIMPORT
4º) A continuación, para ejecutar WSIMPORT tendremos que usar el siguiente comando.
wsimport -keep -p com.universo.soap.client http://localhost:8080/AnotacionSoapApp/ws/hola?wsdl
Básicamente, le estamos indicando dos cosas:
- La ubicación del WSDL del servicio:
- http://localhost:8080/AnotacionesSoapApp/ws/hola?wsdl
- El paquete en el que queremos que se generen las clases:
- com.universo.soap.client
5º) Una vez finalizada la ejecución del WSIMPORT, podremos comprobar que se han generado las clases necesarias en la carpeta correspondiente. Como nosotros hemos indicado el paquete "com.universo.soap.client", pues las clases se habrán creado en el directorio "com\universo\soap\client" que colgará de la carpeta "bin" en la que estamos situados. Por ejemplo, en mi caso las clases se han generado en el directorio:
C:\Program Files\Java\jdk1.8.0_191\bin\com\universo\soap\client
Si habéis seguido los mismos pasos, vosotros también deberíais tener algo parecido a esto:
Como se aprecia, se han creado varias clases a partir del WSDL proporcionado:
- HolaMundoPortType.java
- HolaMundoService.java
- OperacionFault.java
- BaseException.java
- ObjectFactory.java
- package-info.java
La relación entre los servicios Cliente y los servicios Servidor serían más o menos la siguiente:
Revisión de las clases generadas por WSIMPORT
6º) A continuación, vamos a echarle un vistazo al contenido de las clases generadas, para ver cuál es el software añadido por la herramienta WSIMPORT.
✅ HolaMundoPortType: Este es el código de la clase HolaMundoPortType.java. Como vemos, se trata de una Interface que simplemente hace referencia a los métodos implementados por el servicio publicado en el lado del servidor.
// **************************************************
package com.universo.soap.client;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.Action;
import javax.xml.ws.FaultAction;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.9-b130926.1035
* Generated source version: 2.2
*
*/
@WebService(
name = "HolaMundoPortType",
targetNamespace = "http://server.soap.universo.com/")
@SOAPBinding(style = SOAPBinding.Style.RPC)
@XmlSeeAlso({
ObjectFactory.class
})
public interface HolaMundoPortType {
/**
*
* @param nombre
* @return
* returns java.lang.String
* @throws OperacionFault
*/
@WebMethod
@WebResult(name = "saludo", partName = "saludo")
@Action(
input = "http://server.soap.universo.com/HolaMundoPortType/ciaoRequest",
output = "http://server.soap.universo.com/HolaMundoPortType/ciaoResponse",
fault = {
@FaultAction(
className = OperacionFault.class,
value =
"http://server.soap.universo.com/HolaMundoPortType/ciao/Fault/OperacionFault")
})
public String ciao(
@WebParam(name = "nombre", partName = "nombre")
String nombre)
throws OperacionFault
;
}
// **************************************************
✅ HolaMundoService: Este es el contenido de la clase HolaMundoService.java. Esta clase es la que implementa todo el contenido necesario para ejecutar el webservice desde el lado del cliente. Los valores de todos los atributos que aparecen en la clase no se han creado de forma aleatoria, sino que se han generado en función de las indicaciones incluidas en el WSDL del servicio.
// **************************************************
package com.universo.soap.client;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.9-b130926.1035
* Generated source version: 2.2
*
*/
@WebServiceClient(name = "HolaMundoService",
targetNamespace = "http://server.soap.universo.com/",
wsdlLocation = "http://localhost:8080/AnotacionSoapApp/ws/hola?wsdl")
public class HolaMundoService
extends Service
{
private final static URL HOLAMUNDOSERVICE_WSDL_LOCATION;
private final static WebServiceException HOLAMUNDOSERVICE_EXCEPTION;
private final static QName HOLAMUNDOSERVICE_QNAME =
new QName("http://server.soap.universo.com/", "HolaMundoService");
static {
URL url = null;
WebServiceException e = null;
try {
url = new URL("http://localhost:8080/AnotacionSoapApp/ws/hola?wsdl");
} catch (MalformedURLException ex) {
e = new WebServiceException(ex);
}
HOLAMUNDOSERVICE_WSDL_LOCATION = url;
HOLAMUNDOSERVICE_EXCEPTION = e;
}
public HolaMundoService() {
super(__getWsdlLocation(), HOLAMUNDOSERVICE_QNAME);
}
public HolaMundoService(WebServiceFeature... features) {
super(__getWsdlLocation(), HOLAMUNDOSERVICE_QNAME, features);
}
public HolaMundoService(URL wsdlLocation) {
super(wsdlLocation, HOLAMUNDOSERVICE_QNAME);
}
public HolaMundoService(URL wsdlLocation, WebServiceFeature... features) {
super(wsdlLocation, HOLAMUNDOSERVICE_QNAME, features);
}
public HolaMundoService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public HolaMundoService(URL wsdlLocation, QName serviceName,
WebServiceFeature... features) {
super(wsdlLocation, serviceName, features);
}
/**
*
* @return
* returns HolaMundoPortType
*/
@WebEndpoint(name = "HolaMundoPortName")
public HolaMundoPortType getHolaMundoPortName() {
return super.getPort(
new QName("http://server.soap.universo.com/", "HolaMundoPortName"),
HolaMundoPortType.class);
}
/**
*
* @param features
* A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.
* Supported features not in the <code>features</code> parameter will have their default values.
* @return
* returns HolaMundoPortType
*/
@WebEndpoint(name = "HolaMundoPortName")
public HolaMundoPortType getHolaMundoPortName(WebServiceFeature... features) {
return super.getPort(
new QName("http://server.soap.universo.com/", "HolaMundoPortName"),
HolaMundoPortType.class, features);
}
private static URL __getWsdlLocation() {
if (HOLAMUNDOSERVICE_EXCEPTION!= null) {
throw HOLAMUNDOSERVICE_EXCEPTION;
}
return HOLAMUNDOSERVICE_WSDL_LOCATION;
}
}
// **************************************************
✅ OperacionFault: Este es el contenido de la clase OperacionFault.java. Esta clase es la que realiza la gestión de los Fault generados por el webservice ubicado en el servidor.
// **************************************************
package com.universo.soap.client;
import javax.xml.ws.WebFault;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.9-b130926.1035
* Generated source version: 2.2
*
*/
@WebFault(name = "operationfault", targetNamespace = "http://server.soap.universo.com/")
public class OperacionFault
extends Exception
{
/**
* Java type that goes as soapenv:Fault detail element.
*
*/
private BaseException faultInfo;
/**
*
* @param faultInfo
* @param message
*/
public OperacionFault(String message, BaseException faultInfo) {
super(message);
this.faultInfo = faultInfo;
}
/**
*
* @param faultInfo
* @param cause
* @param message
*/
public OperacionFault(String message, BaseException faultInfo, Throwable cause) {
super(message, cause);
this.faultInfo = faultInfo;
}
/**
*
* @return
* returns fault bean: com.universo.soap.client.BaseException
*/
public BaseException getFaultInfo() {
return faultInfo;
}
}
// **************************************************
✅ BaseException: Este es el contenido de la clase BaseException.java. Esta clase es la que contiene la estructura de las excepciones devueltas por los Fault del webservice.
// **************************************************
package com.universo.soap.client;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Clase Java para baseException complex type.
*
* <p>El siguiente fragmento de esquema especifica el contenido que se espera
* que haya en esta clase.
*
* <pre>
* <complexType name="baseException">
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="errorDesc" type="{http://www.w3.org/2001/XMLSchema}string"
* minOccurs="0"/>
* <element name="errorID" type="{http://www.w3.org/2001/XMLSchema}string"
* minOccurs="0"/>
* <element name="errorType" type="{http://www.w3.org/2001/XMLSchema}string"
* minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "baseException", propOrder = {
"errorDesc",
"errorID",
"errorType"
})
public class BaseException {
protected String errorDesc;
protected String errorID;
protected String errorType;
/**
* Obtiene el valor de la propiedad errorDesc.
*
* @return
* possible object is
* {@link String }
*
*/
public String getErrorDesc() {
return errorDesc;
}
/**
* Define el valor de la propiedad errorDesc.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setErrorDesc(String value) {
this.errorDesc = value;
}
/**
* Obtiene el valor de la propiedad errorID.
*
* @return
* possible object is
* {@link String }
*
*/
public String getErrorID() {
return errorID;
}
/**
* Define el valor de la propiedad errorID.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setErrorID(String value) {
this.errorID = value;
}
/**
* Obtiene el valor de la propiedad errorType.
*
* @return
* possible object is
* {@link String }
*
*/
public String getErrorType() {
return errorType;
}
/**
* Define el valor de la propiedad errorType.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setErrorType(String value) {
this.errorType = value;
}
}
// **************************************************
✅ ObjectFactory: Este es el contenido de la clase ObjectFactory.java. Esta clase nos permitirá crear instancias para cualquiera de las clases contenidas en el paquete com.universo.soap.client.
// **************************************************
package com.universo.soap.client;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
/**
* This object contains factory methods for each
* Java content interface and Java element interface
* generated in the com.universo.soap.client package.
* <p>An ObjectFactory allows you to programatically
* construct new instances of the Java representation
* for XML content. The Java representation of XML
* content can consist of schema derived interfaces
* and classes representing the binding of schema
* type definitions, element declarations and model
* groups. Factory methods for each of these are
* provided in this class.
*
*/
@XmlRegistry
public class ObjectFactory {
private final static QName _Operationfault_QNAME =
new QName("http://server.soap.universo.com/", "operationfault");
/**
* Create a new ObjectFactory that can be used to create new instances of schema
* derived classes for package: com.universo.soap.client
*
*/
public ObjectFactory() {
}
/**
* Create an instance of {@link BaseException }
*
*/
public BaseException createBaseException() {
return new BaseException();
}
/**
* Create an instance of {@link JAXBElement }{@code <}{@link BaseException }{@code >}}
*
*/
@XmlElementDecl(namespace = "http://server.soap.universo.com/", name = "operationfault")
public JAXBElement<BaseException> createOperationfault(BaseException value) {
return new JAXBElement<BaseException>(_Operationfault_QNAME, BaseException.class,
null, value);
}
}
// **************************************************
✅ package-info: Este es el contenido de la clase package-info.java.
// **************************************************
@javax.xml.bind.annotation.XmlSchema(namespace = "http://server.soap.universo.com/")
package com.universo.soap.client;
// **************************************************
Creación de Proyecto Cliente
7º) Llegados a este punto, ahora nos toca crearnos un proyecto para alojar las clases generadas por la herramientoa WSIMPORT. Para ello, nos creamos un proyecto de tipo "Dynamic Web Project" en Eclipse.
A continuación, marcamos el proyecto y seleccionamos la opción CONFIGURE - CONVERT TO MAVEN PROJECT.
Se nos debería quedar una estructura del siguiente tipo:
8º) Nos creamos un paquete denominado "com.universo.soap.client" (recordad, este fue el paquete que indicamos al lanzar la generación de clases mediante WSIMPORT). Dentro de dicho paquete copiamos las clases previamente generadas:
- HolaMundoPortType.java
- HolaMundoService.java
- OperacionFault.java
- BaseException.java
- ObjectFactory.java
- package-info.java
Tras estas acciones, la estructura de nuestro proyecto quedará del siguiente modo.
9º) A continuación, añadimos el proyecto al TOMCAT y procedemos a arrancar el servidor. Si hemos seguido los pasos correctamente, el arranque debería completarse sin errores.
jul 09, 2021 9:48:25 PM org.apache.catalina.core.StandardEngine startInternal
INFORMACIÓN: Starting Servlet engine: [Apache Tomcat/9.0.36]
jul 09, 2021 9:48:25 PM org.apache.jasper.servlet.TldScanner scanJars
INFORMACIÓN: Al menos un JAR, que se ha explorado buscando TLDs, aún no contenía TLDs. Activar historial de depuración para este historiador para una completa lista de los JARs que fueron explorados y de los que nos se halló TLDs. Saltarse JARs no necesarios durante la exploración puede dar lugar a una mejora de tiempo significativa en el arranque y compilación de JSP .
jul 09, 2021 9:48:26 PM org.apache.jasper.servlet.TldScanner scanJars
INFORMACIÓN: Al menos un JAR, que se ha explorado buscando TLDs, aún no contenía TLDs. Activar historial de depuración para este historiador para una completa lista de los JARs que fueron explorados y de los que nos se halló TLDs. Saltarse JARs no necesarios durante la exploración puede dar lugar a una mejora de tiempo significativa en el arranque y compilación de JSP .
jul 09, 2021 9:48:26 PM org.apache.coyote.AbstractProtocol start
INFORMACIÓN: Starting ProtocolHandler ["http-nio-8080"]
jul 09, 2021 9:48:26 PM org.apache.catalina.startup.Catalina start
INFORMACIÓN: Server startup in [572] milliseconds
Probando el Webservice SOAP
10º) A continuación, para probar el servicio SOAP, vamos a crearnos una clase que lance una invocación contra el webservice situado en el lado del servidor. Para ello, nos situamos en el paquete "com.universo.soap.client" y nos creamos una clase HolaMundoTest.java con el siguiente contenido.
// **************************************************
package com.universo.soap.client;
public class HolaMundoTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
HolaMundoService service = new HolaMundoService();
HolaMundoPortType holamundo = service.getHolaMundoPortName();
String nombre = "Benedetta";
String respuesta = "";
try {
respuesta = holamundo.ciao(nombre);
} catch (OperacionFault e) {
// TODO Auto-generated catch block
System.out.println("Excepcion ---> " + e.getFaultInfo().getErrorDesc());
}
System.out.println(respuesta);
}
}
// **************************************************
11º) Finalmente, ejecutamos la clase HolaMundoTest.java. Si tenemos todo correctamente implementado (tanto en el proyecto cliente como en el proyecto servidor), el resultado debería ser el siguiente.
Si probamos a enviar el nombre en blanco (String nombre = "") y volvemos a ejecutar la clase HolaMundoTest.java, esta vez deberíamos obtener un Fault.
👉 Con el proceso indicado en este artículo (y con el especificado en el
artículo en el que contábamos cómo se creaba un servicio SOAP en el lado
del servidor), ya debemos tener una idea bastante clara de cómo se crea un webservice mediante anotaciones y WsImport. De hecho, en el ejemplo hemos publicado un servicio que incluso implementaba su correspondiente Fault, de manera que nos servirá de guía para cuando queramos generar excepciones en un webservice. Quizás en un futuro postee un ejemplo más completo pero, por ahora, lo que hemos visto hoy nos servirá para empezar a jugar.
Saludos.
Comentarios
Publicar un comentario