Para la programación de servicios web, independientemente de la tecnología, ya sea Java, ya sea .NET, se pueden utilizar dos enfoques distintos; crear el servicio web a partir de un WSDL ya existente (enfoque top-down o descendente), o generar el correspondiente WSDL a partir de código fuente ya existente (enfoque bottom-up o ascendente).
El objetivo de esta entrada es generar un servicio web mediante el enfoque bottom-up o ascendente y utilizando la tecnología JAXWS. En concreto, en esta entrada se utilizan las anotaciones de la especificación JSR-181.
¿Qué necesitas para seguir esta entrada?
Me gustaría aclarar que no voy a utilizar para la generación de este servicio web ninguna ayuda (wizard) de las que proporcionan las IDEs Java que hay en el mercado, por tanto, cualquiera de éstas herramientas sirve para desarrollar el ejemplo.
En este caso, y porque ya lo tengo instalado, voy a utilizar Eclipse 3.7.1 (Helios) con el plugin de Oracle (OEPE) para realizar un rápido despliegue en un dominio básico de Weblogic que he creado para esta ocasión.
El servidor de aplicaciones sobre el que voy a desplegar y publicar el servicio web va a ser el parche 10.3.6 de Weblogic 11g.
El dominio básico de Weblogic creado para estas pruebas tiene una única instancia que nos servirá tanto para publicar la consola administrativa del servidor, como la aplicación de ejemplo que voy a generar.
La JDK instalada es la versión 1.6.0_29 del Hotspot de Oracle.
Los componentes de la prueba
Los servicios web, en java, se publican en aplicaciones web dinámicas y se empaquetan en ficheros WAR. Teniendo esto en cuenta, para esta prueba voy a crear un proyecto Web Dinámico que a su vez voy a incluir en una aplicación JEE empaquetada en un EAR.
El motivo de incluir el módulo WAR en un EAR es simplemente una decisión personal del grupo «porquemeapetece«. En realidad, simplemente con el módulo WAR ya nos bastaría para desplegar y publicar el servicio web en el servidor de Weblogic.
La aplicación EAR la voy a llamar AppEjemploEAR y el módulo WAR lo voy a llamar JaxWSEjemploWAR.
La clase java de ejemplo.
La lógica de negocio que quiero convertir en servicio web es una calculadora muy básica compuesta por cuatro métodos: suma, resta, multiplicacion y division, que realizan las operaciones aritméticas correspondientes entre dos operandos.
La implementación de este servicio es la clase CalculadoraService.java que se encuentra en el directorio de fuentes de la aplicación WAR.
package ejemplo.jaxws;
/**
* @author EGV
* <p>Clase calculadora que proporciona las cuatro operaciones aritméticas
* básicas: suma, resta, multiplicación y división.</p>
*/
public class CalculadoraService {
/**
* Realiza la suma de los operandos.
* @param operando1 Operando uno.
* @param operando2 Operando dos.
* @return Operando1 + Operando2.
*/
public Integer suma (Integer operando1, Integer operando2)
{
return operando1 + operando2;
}
/**
* Realiza la resta de los dos operandos.
* @param operando1 Operando uno.
* @param operando2 Operando dos.
* @return Operando1 - Operando2
*/
public Integer resta (Integer operando1, Integer operando2)
{
return operando1 - operando2;
}
/**
* Realiza la multiplicación de los dos operandos.
* @param operando1 Operando uno.
* @param operando2 Operando dos.
* @return Operando1 * Operando2
*/
public Integer multiplicacion (Integer operando1, Integer operando2)
{
return operando1 * operando2;
}
/**
* Realiza la división entre los operandos.
* @param operando1 Operando1
* @param operando2 Operando2
* @return Operando1 / Operando2
*/
public Integer division (Integer operando1, Integer operando2)
{
return operando1 / operando2;
}
/**
* @param args
*/
public static void main(String[] args) {
Integer operando1 = new Integer(10);
Integer operando2 = new Integer(3);
Integer resultado;
CalculadoraService calculadora = new CalculadoraService();
StringBuffer bsTraza;
resultado = calculadora.suma(operando1, operando2);
bsTraza = new StringBuffer();
bsTraza.append(operando1).append("+").append(operando2).append("=").append(resultado);
System.out.println(bsTraza.toString());
resultado = calculadora.resta(operando1, operando2);
bsTraza = new StringBuffer();
bsTraza.append(operando1).append("-").append(operando2).append("=").append(resultado);
System.out.println(bsTraza.toString());
resultado = calculadora.multiplicacion(operando1, operando2);
bsTraza = new StringBuffer();
bsTraza.append(operando1).append("*").append(operando2).append("=").append(resultado);
System.out.println(bsTraza.toString());
resultado = calculadora.division(operando1, operando2);
bsTraza = new StringBuffer();
bsTraza.append(operando1).append("/").append(operando2).append("=").append(resultado);
System.out.println(bsTraza.toString());
}
}
Las anotaciones básicas para definir Servicios Web.
La especificación JSR-181 define una serie de anotaciones que permiten generar, a partir de una clase java, un servicio web. Gracias a estas anotaciones el desarrollo del servicio web se simplifica enormemente, además, las anotaciones de esta especificación están incluidas en la JDK a partir de la versión 1.5 y sucesoras, por tanto, incluidas en mi versión 1.6.0_29.
Las anotaciones que voy a utilizar son las siguientes:
- @WebService –> javax.jws.WebService
- @WebMethod –> javax.jws.WebMethod
- @WebParam –> javax.jws.WebParam
- @WebResult –> javax.jws.WebResult
- @SOAPBinding –> javax.jws.soap.SOAPBinding
Las anotaciones @WebParam, @WebResult y @SOAPBinding las incluyo para dejar constancia de su funcionamiento, en verdad para este ejemplo tan básico no serían necesarias ya que la configuración por defecto, es la más adecuada.
La clase java de ejemplo anotada.
La clase CalculadoraService.java con las anotaciones quedaría de la siguiente forma:
package ejemplo.jaxws; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; /** * @author EGV * <p>Clase calculadora que proporciona las cuatro operaciones aritméticas * básicas: suma, resta, multiplicación y división.</p> * <p>Esta clase se anota con la especificación JSR-181 para convertirla * en un servicio web.</p> */ @WebService(serviceName="CalculadoraService", name="CalculadoraPortType", targetNamespace="http://egv.com/ejemplo-jaxws/calculadora") @SOAPBinding( style=SOAPBinding.Style.DOCUMENT, use=SOAPBinding.Use.LITERAL, parameterStyle=SOAPBinding.ParameterStyle.WRAPPED) public class CalculadoraService { /** * Realiza la suma de los operandos. * @param operando1 Operando uno. * @param operando2 Operando dos. * @return Operando1 + Operando2. */ @WebMethod( operationName="suma", action="sumaAction") @WebResult( name="sumaResponse") public Integer suma ( @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1, @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2) { return operando1 + operando2; } /** * Realiza la resta de los dos operandos. * @param operando1 Operando uno. * @param operando2 Operando dos. * @return Operando1 - Operando2 */ @WebMethod( operationName="resta", action="restaAction") @WebResult( name="restaResponse") public Integer resta ( @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1, @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2) { return operando1 - operando2; } /** * Realiza la multiplicación de los dos operandos. * @param operando1 Operando uno. * @param operando2 Operando dos. * @return Operando1 * Operando2 */ @WebMethod( operationName="multiplicacion", action="multiplicacionAction") @WebResult( name="multiplicacionResponse") public Integer multiplicacion ( @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1, @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2) { return operando1 * operando2; } /** * Realiza la división entre los operandos. * @param operando1 Operando1 * @param operando2 Operando2 * @return Operando1 / Operando2 */ @WebMethod( operationName="division", action="divisonAction") @WebResult( name="divisionResponse") public Integer division ( @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1, @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2) { return operando1 / operando2; }
Las anotaciones @WebService y @SOAPBinding son generales al servicio web generado, por lo que se asocian a la clase. Las anotaciones @WebMethod y @WebResult están relacionadas con el método y por tanto hay una por cada método a publicar. La anotación @WebParam está relacionada a los parámetros, por lo que habrá una por cada parámetro que haya en los métodos.
Desplegando, publicando y probando el servicio web.
Una vez que la clase java que queremos publicar como servicio web está anotada, desplegamos la aplicación EAR en el servidor de Weblogic.
Si hemos hecho correctamente la anotación, el servidor desplegará AppEjemploEAR con el módulo JaxWSEjemploWAR dentro y el servicio web CalculadoraService publicado.

Para probar el servicio web se puede hacer de varias maneras; generando un cliente de web service, utilizando la herramienta de test de servicios web proporcionada por Weblogic, utilizando herramientas externas como SOAPUi, etcétera.
En la imagen se puede ver el cliente que proporciona el servidor de Weblogic. Si en la vista de despliegue se pincha en el servicio web a probar, en este caso CalculadoraService, nos abrirá la pantalla con la configuración propia de este servicio. En la pestaña de Prueba existe un Punto de Prueba que se llama Test
Client que nos llevará a una página web que nos permitirá probar las diferentes operaciones de CalculadoraService.

El WSDL generado
El WSDL del servicio web es generado a partir de la clase anotada.
Podemos observar como las anotaciones que hemos incluido en el código Java se reflejan en los diferentes apartados del documento WSDL.
<!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is Oracle JAX-WS 2.1.5. --> <!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is Oracle JAX-WS 2.1.5. --> <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://egv.com/ejemplo-jaxws/calculadora" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://egv.com/ejemplo-jaxws/calculadora" name="CalculadoraService"> <types> <xsd:schema> <xsd:import namespace="http://egv.com/ejemplo-jaxws/calculadora" schemaLocation="http://172.16.204.140:7001/JaxWSEjemploWAR/CalculadoraService?xsd=1"/> </xsd:schema> </types> <message name="suma"> <part name="parameters" element="tns:suma"/> </message> <message name="sumaResponse"> <part name="parameters" element="tns:sumaResponse"/> </message> <message name="resta"> <part name="parameters" element="tns:resta"/> </message> <message name="restaResponse"> <part name="parameters" element="tns:restaResponse"/> </message> <message name="multiplicacion"> <part name="parameters" element="tns:multiplicacion"/> </message> <message name="multiplicacionResponse"> <part name="parameters" element="tns:multiplicacionResponse"/> </message> <message name="division"> <part name="parameters" element="tns:division"/> </message> <message name="divisionResponse"> <part name="parameters" element="tns:divisionResponse"/> </message> <portType name="CalculadoraPortType"> <operation name="suma"> <input message="tns:suma"/> <output message="tns:sumaResponse"/> </operation> <operation name="resta"> <input message="tns:resta"/> <output message="tns:restaResponse"/> </operation> <operation name="multiplicacion"> <input message="tns:multiplicacion"/> <output message="tns:multiplicacionResponse"/> </operation> <operation name="division"> <input message="tns:division"/> <output message="tns:divisionResponse"/> </operation> </portType> <binding name="CalculadoraPortTypePortBinding" type="tns:CalculadoraPortType"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="suma"> <soap:operation soapAction="sumaAction"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="resta"> <soap:operation soapAction="restaAction"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="multiplicacion"> <soap:operation soapAction="multiplicacionAction"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="division"> <soap:operation soapAction="divisonAction"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="CalculadoraService"> <port name="CalculadoraPortTypePort" binding="tns:CalculadoraPortTypePortBinding"> <soap:address location="http://172.16.204.140:7001/JaxWSEjemploWAR/CalculadoraService"/> </port> </service> </definitions>
Los XML Request y Response de la operación Suma.
Para invocar a la operación Suma del servicio web CalculadoraService se envía el XML de petición de servicio. Como respuesta, el servicio web enviará otro XML con la información de la operación. Estos XMLs también están influidos por las anotaciones de la clase Java anotada.
XML Request
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> <env:Header /> <env:Body> <suma xmlns="http://egv.com/ejemplo-jaxws/calculadora"> <!--Optional:--> <operando1 xmlns="">10</operando1> <!--Optional:--> <operando2 xmlns="">13</operando2> </suma> </env:Body> </env:Envelope>
XML Response
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> <ns2:sumaResponse xmlns:ns2="http://egv.com/ejemplo-jaxws/calculadora"> <sumaResponse>23</sumaResponse> </ns2:sumaResponse> </S:Body> </S:Envelope>
Conclusión
Links
- Introducción al fichero de definición de servicios web o WSDL (https://eduvitoriatecnicomio.wordpress.com/2013/01/30/wsdl-i-introduccion/)
- Especificación JSR 181 – Web Services Metadata for the Java Platform (http://jcp.org/en/jsr/detail?id=181)
- Referencia de JWS Annotation en la documentación de Oracle (http://docs.oracle.com/cd/E13222_01/wls/docs103/webserv_ref/annotations.html#wp1105627)