En este nuevo tutorial vamos a ver como crear un Web Service a partir del wsdl con Axis2. El wsdl es un xml schema en el que se describe el WS, los métodos que son accesibles, y el formato de los mensajes SOAP que acepta. Tienes más información aquí.

Para crear un WS, siempre deberíamos empezar escribiendo el wsdl, para pasar después a picar el código de la lógica de negocio que estará en la clase java que recibirá los datos en el WS. De esta forma tendremos controlado los mensajes SOAP que se envían y reciben en nuestro WS, y nos evitaremos problemas a posteriori.

En nuestro ejemplo, vamos a hacer un webservice que permita realizar búsquedas de viajes. No vamos a tener conexión con ninguna base de datos, así que simularemos esta conexión mediante otra clase.

Los pasos que vamos a seguir para crear el WS serán los siguientes:

  • Escribir el wsdl.
  • Utilizar el script wsdl2java de Axis2 para crear las clases y el services.xml del WS a partir del wsdl.
  • Escribir la lógica de negocio.

Creación del wsdl

Nuestro ejemplo tendrá un método llamado “consultar”, y los parámetros que se le podrán pasar serán los siguientes:

  • Origen
  • Destino
  • Fecha

Recibiremos los siguientes datos de cada viaje que recuperemos con la búsqueda:

  • Referencia
  • Agencia
  • Origen
  • Destino
  • Fecha
  • Hora salida
  • Hora llegada

El wsdl quedaría así:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://ejemplo.namespace" targetNamespace="http://ejemplo.namespace">
<wsdl:types>
<xs:schema targetNamespace="http://new.webservice.namespace" elementFormDefault="qualified">
<xs:element name="consulta">
<xs:complexType>
<xs:sequence>
<xs:element name="origen" type="xs:string" minOccurs="0"/>
<xs:element name="destino" type="xs:string" minOccurs="0"/>
<xs:element name="fecha" type="xs:date" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="respuesta">
<xs:complexType>
<xs:sequence>
<xs:element name="viajes" type="tns:viaje" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="viaje">
<xs:sequence>
<xs:element name="referencia" type="xs:string"/>
<xs:element name="agencia" type="xs:string"/>
<xs:element name="origen" type="xs:string"/>
<xs:element name="destino" type="xs:string"/>
<xs:element name="fecha" type="xs:date"/>
<xs:element name="horaSalida" type="xs:string"/>
<xs:element name="horaLlegada" type="xs:string"/>
</xs:sequence> </xs:complexType> </xs:schema>
</wsdl:types> <wsdl:message name="ConsultarRequest">
<wsdl:part name="parameter" element="tns:consulta"/>
</wsdl:message>
<wsdl:message name="ConsultarResponse">
<wsdl:part name="parameter" element="tns:respuesta"/>
</wsdl:message>
<wsdl:portType name="viajes">
<wsdl:operation name="consultar">
<wsdl:input message="tns:ConsultarRequest"/>
<wsdl:output message="tns:ConsultarResponse"/>
</wsdl:operation> </wsdl:portType>
<wsdl:binding name="viajesSOAP" type="tns:viajes">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="consultar">
<soap:operation soapAction="consultar" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="ejemplo">
<wsdl:port name="viajesSOAP" binding="tns:viajesSOAP">
<soap:address location="http://localhost:8080/ejemplo/viajes"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

Creación de las clases java a partir del wsdl

Ahora que ya hemos escrito nuestro wsdl, vamos a crear las clases que implementarán el WS. Deberemos tener instalado el Axis2 en nuestro equipo. Si aún no lo tienes, puedes consultar éste otro tutorial en el que te contábamos los pasos a seguir.

Si vamos a utilizar algún IDE para escribir el código, conviene en este punto crearnos un nuevo proyecto java (no tiene que ser proyecto web). Crea una carpeta que se llame “META-INF” y copia el wsdl dentro de dicha carpeta.

Si estamos utilizando Eclipse, existe un plugin que nos será de gran utilidad para esta tarea. Se llama “Code Generator Wizard” y lo podéis descargar desde aquí. Con este plugin nos ahorramos el tener que recurrir a la línea de comandos para ejecutar el script wsdl2java. De todos modos, veremos las dos formas.

Generar el código desde la línea de comandos

Como ya comentamos en el anterior tutorial, conviene tener incluído en el PATH de nuestro equipo la ruta de Axis2/bin, para que podamos ejecutar el script desde cualquier directorio.

Abrimos una consola de comandos, y nos situamos en la ruta de nuestro proyecto. En nuestro ejemplo es:

C:\eclipse\workspacePruebas\ejemplo

Si ejecutas el script wsdl2java, saldrá un listado con todos los parámetros que admite.

En nuestro ejemplo, la llamada al script quedaría así:

wsdl2java -o . -ss -sd -ssi --noBuildXML --noWSDL -or -S ./src -R ./META-INF -uri ./META-INF/viajes.wsdl

donde:

  • -o Indica el path de salida.
  • -ss Indica que se genere el código para el servidor.
  • -sd Indica que se genere el fichero “services.xml”.
  • -ssi Indica que se genere un interfaz para la implementación del servicio.
  • --noBuildXML Indica que no se genere el fichero Build.xml (en nuestro caso como no vamos a utilizar Ant no lo necesitamos).
  • --noWSDL Indica que no genere de nuevo el wsdl (ya lo tenemos).
  • -or Indica que se sobreescriban los ficheros en caso de existir.
  • -S Indica el directorio en el que se generarán los fuentes.
  • -R Indica el directorio en el que se dejarán los ficheros de recursos (en nuestro caso el services.xml).
  • -uri Indica el wsdl a partir del cúal se van a crear las clases java.

Pantalla de ejecución del script wsdl2java de Axis2

Con esto se nos debería haber generado el fichero services.xml en la carpeta META-INF, y las clases java del WS en la carpeta src.

Si pruebas a compilar el proyecto, te darán errores. Tienes que añadir al proyecto las librerías de Axis2. Están en la carpeta lib, en el directorio en el esté instalado Axis2. Si utilizas Eclipse, es una buena idea crear una librería de usuario, en la que metas todos los jar de Axis2. Si lo utilizas en otro proyecto, sólo tendrás que importar la librería:

Pantalla de librerías de usuario de Eclipse

Generar las clases java con el plugin “Code Generator Wizard” de Eclipse

Si estás utilizando Eclipse, te recomendamos que utilices este plugin para generar el wsdl. Una vez descargado descomprímelo en la carpeta plugins dentro del directorio de instalación del Eclipse. Si lo tienes arrancado, deberás reiniciarlo para que lo cargue.

Una vez instalado, pinchamos en nuestro proyecto, y seleccionamos “Nuevo” y “Otro…”. Si tienes bien instalado el plugin deberías tener una opción como la siguiente:

Pantalla de selección de tipo de acción del Code Generator

Seleccionamos la opción de generar el código fuente java a partir de un fichero wsdl. En la siguiente ventana seleccionamos nuestro fichero wsdl:

Pantalla de selección de wsdl del Code Generator

Ahora tenemos que configurar el código que queremos generar. Veamos con calma las diferentes opciones:

Pantalla de configuración del Code Generator

Seleccionamos la opción “custom” en el “Codegen option”, para poder modificar el resto de parámetros. Tenemos:

  • Output language: podemos generar el código del WS en java y en C. En nuestro caso dejamos java.
  • Service name: en caso de que tengamos varios servicios en el wsdl, podremos especificar para cúal queremos generar el código.
  • Port name: lo mismo, pero para el caso de los puertos.
  • Databinding name: databiding que queremos utilizar.
  • Custom package name: por si queremos darle un nombre específico a los paquetes java.
  • Generate case test: si generamos código para crear un cliente del WS, tenemos la opción de crear una clase de test, para hacer pruebas unitarias.
  • Generate client class code: mediante esta opción le podremos indicar que nos cree el código de la aplicación cliente del WS. Podemos especificar si queremos que nos genere código para peticiones síncronas, asíncronas o las dos. En las peticiones síncronas el cliente se quedará esperando a que el WS devuelva la respuesta, mientras que en las asíncronas el cliente sigue ejecutandose, y la respuesta se “captura” cuando el WS la envía.
  • Generate server side code: genera el código de nuestro WS. Le podemos indicar que nos genere el fichero services.xml y que nos cree una interfaz para el Skeleton. Esto último es para que la clase que implementará la lógica de negocio implemente dicha interfaz.
  • La última opción nos indica que se genere el código del cliente y del servidor.
  • Al final aparecen una lista de los namespaces que tiene el wsdl, y el paquete en el que irán las clases de dicho namespace, por si quieres ajustarlo.

En nuestro ejemplo, simplemente crearemos el código del lado del servidor, y le decimos que genere el services.xml y la interfaz para la clase Skeleton. Podéis verlo en la imagen de arriba.

En la última pantalla le indicaremos el lugar en el que queremos que nos deje el código fuente generado. Para no tener que importar los jars de axis2, podemos decirle que nos los incluya él con la opción “Add Axis2 libraries to the codegen result proyect”:

Pantalla de selección de path de salida para los ficheros generados Code Generator

Con esto hemos terminado de generar las clases java que necesitamos.

Añadir la lógica de negocio al WS

Ahora que ya tenemos las clases del WS, tenemos que incluir la lógica de negocio de nuestro WS. Entre las clases que se han generado, habrá una que se llame “nombre”Skeleton. En nuestro ejemplo se llama EjemploSkeleton. En esa clase es donde tendremos que escribir la lógica de negocio de nuestro WS.

Tenemos la clase Datos, que es la que simula la conexión con base de datos, y la de EjemploSkeleton, que es la que recibe la llamada. En el resto de clases que ha creado Axis2 no tenemos que tocar nada.

Echemos un vistazo al código de estas dos clases:

Datos.java

package namespace.ejemplo;
 
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Vector;
 
public class Datos {
 
Viaje[] viajes = new Viaje[4];
 
// En el constructor metemos 4 viajes de ejemplo
public Datos() {
super();
Viaje viaje = null;
 
viaje = new Viaje();
viaje.setReferencia("REF-000001");
viaje.setAgencia("Sol");
viaje.setDestino("Málaga");
viaje.setOrigen("Madrid");
viaje.setFecha(new GregorianCalendar(2008, 6, 5).getTime());
viaje.setHoraSalida("12:00");
viaje.setHoraLlegada("18:00");
viajes[0] = viaje;
 
viaje = new Viaje();
viaje.setReferencia("REF-000002");
viaje.setAgencia("Luna");
viaje.setDestino("Barcelona");
viaje.setOrigen("Bilbao");
viaje.setFecha(new GregorianCalendar(2008, 6, 6).getTime());
viaje.setHoraSalida("09:00");
viaje.setHoraLlegada("14:00");
viajes[1] = viaje;
 
viaje = new Viaje();
viaje.setReferencia("REF-000003");
viaje.setAgencia("Sol");
viaje.setDestino("Sevilla");
viaje.setOrigen("Madrid");
viaje.setFecha(new GregorianCalendar(2008, 6, 7).getTime());
viaje.setHoraSalida("15:00");
viaje.setHoraLlegada("21:00");
viajes[2] = viaje;
 
viaje = new Viaje();
viaje.setReferencia("REF-000004");
viaje.setAgencia("Luna");
viaje.setDestino("Lugo");
viaje.setOrigen("Valladolid");
viaje.setFecha(new GregorianCalendar(2008, 6, 8).getTime());
viaje.setHoraSalida("01:00");
viaje.setHoraLlegada("04:00");
viajes[3] = viaje;
}
 
public Viaje[] consultar(String origen, String destino, Date fecha) {
boolean cumple;
Viaje[] respuesta = null;
Vector&lt;Viaje&gt; temp = new Vector&lt;Viaje&gt;();
int cont = 0;
 
for (int i= 0; i &lt; viajes.length; i++) {
cumple = true;
 
if (origen != null) {
if (!viajes[i].getOrigen().equalsIgnoreCase(origen)) cumple = false;
}
 
if (destino != null &amp;&amp; cumple) {
if (!viajes[i].getDestino().equalsIgnoreCase(destino)) cumple = false;
}
 
if (fecha != null &amp;&amp; cumple) {
GregorianCalendar fechaConsulta = new GregorianCalendar();
GregorianCalendar fechaViaje = new GregorianCalendar();
fechaConsulta.setTime(fecha);
fechaViaje.setTime(viajes[i].getFecha());
 
if (fechaConsulta.get(GregorianCalendar.DATE) != fechaViaje.get(GregorianCalendar.DATE) ||
fechaConsulta.get(GregorianCalendar.MONTH) != fechaViaje.get(GregorianCalendar.MONTH) ||
fechaConsulta.get(GregorianCalendar.YEAR) != fechaViaje.get(GregorianCalendar.YEAR)) {
cumple = false;
}
}
 
if (cumple) {
temp.add(viajes[i]);
cont++;
}
}
 
if (cont &gt; 0) {
respuesta = new Viaje[cont];
respuesta = temp.toArray(respuesta);
}
 
return respuesta;
}
 
}

EjemploSkeleton.java

/**
* EjemploSkeleton.java
*
* This file was auto-generated from WSDL
* by the Apache Axis2 version: 1.4  Built on : Apr 26, 2008 (06:24:30 EDT)
*/
package namespace.ejemplo;
/**
*  EjemploSkeleton java skeleton for the axisService
*/
public class EjemploSkeleton implements EjemploSkeletonInterface{
 
/**
* Auto generated method signature
*
* @param consulta0
*/
 
public namespace.ejemplo.Respuesta consultar(namespace.ejemplo.Consulta consulta)
{
namespace.ejemplo.Respuesta respuesta = new namespace.ejemplo.Respuesta();
 
// Creamos la clase que simula la conexión con base de datos
Datos datos = new Datos();
 
// Consultamos los viajes que cumplen el criterio de búsqueda
respuesta.setViajes(datos.consultar(consulta.getOrigen(), consulta.getDestino(), consulta.getFecha()));
 
return respuesta;
}
 
}

Con esto ya tenemos todo listo. Nos queda crear nuestro fichero .aar y probar que el WS se despliega correctamente. Para crear el fichero, es el mismo proceso que crear un jar, pero le cambiamos la extensión por aar. Este fichero se puede abrir con el winrar o similar, y podréis ver como ha quedado la estructura de directorios. En nuestro ejemplo queda así:

  • META-INF
    • services.xml
    • viajes.wsdl
  • namespace
    • ejemplo
      • Consulta$1.class
      • Consulta$Factory.class
      • Consulta.class
      • Datos.class
      • EjemploMessageReceiverInOut.class
      • EjemploSkeleton.class
      • EjemploSkeletonInterface.class
      • ExtensionMapper.class
      • Respuesta$1.class
      • Respuesta$Factory.class
      • Respuesta.class
      • Viaje$1.class
      • Viaje$Factory.class
      • Viaje.class

Como podéis ver, aún estando utilizando la versión 1.4 de Axis2, siguen apareciendo las “bonitas” clases $x. La verdad es q el código q genera Axis2 no es muy bueno. Estará lleno de warnings, sin indentar, y será prácticamente ilegible. La parte buena es que de todo ese código en principio no tendréis que tocar nada.

Probar el WS

Una vez llegados a este punto, lo habrá que comprobar si el WS que hemos creado se despliega correctamente en un servidor de aplicaciones o no. Una de las cosas buenas que tiene Axis2 es que incorpora un “microservidor” de aplicaciones, con el que podemos testear si lo que hemos hecho está bien o no. Éste servidor está configurado para que utilice por defecto el puerto 8080. Si ya estáis utilizando este puerto, podéis cambiar este parámetro dentro del fichero “axis2.xml” que se encuentra en el directorio “conf” dentro del directorio de instalación de Axis2. Cambiar el parámetro:

<parameter name=”port”>8080</parameter> dentro del <transportReceiver name=”http”

Nos vamos al directorio en el que tenemos Axis2 instalado, y entramos en la carpeta “repository/services”. En esta carpeta dejaremos nuestro aar.

Ahora volvemos a la carpeta del Axis2, y dentro de “bin” ejecutamos el programa “axis2server”. Se os abrirá una ventana en la que podréis ver si el WS se ha desplegado correctamente o si ha habido errores:

Pantalla de arranque del Axis2server

Abrir una ventana en el navegador, y escribir la dirección:

http://localhost:8080/axis2/services/

Si todo ha ido bien, en la lista de servicios debería aparecer vuestro WS.

Ahora que ya hemos visto como crear WS con Axis2, para los próximos tutoriales vamos a empezar a ver cómo utilizar JaxWS, así que nos despedimos hasta el próximo tutorial.