Ejemplo sencillo de web service con CXF
Preparación del entorno[editar]
Veamos un ejemplo muy sencillo de cómo crear un Web Service y un cliente con la librería apache CXF. Lo primero, por supuesto, bajarse esa librería http://cxf.apache.org/download.html . No es más que un zip que debemos desempaquetar en cualquier sitio de nuestro disco duro.
Ahora, desde nuestro IDE favorito (eclipse por ejemplo), creamos un proyecto java normal. Una vez creado, añadimos al proyecto todas las librerías .jar que vienen en el directorio lib de CXF ($CXF_HOME/lib/*.jar). No nos harán falta todas, pero esto es más fácil que ir buscando una a una las que nos hacen falta exactamente.
La interfaz del Web Service[editar]
Podemos crear directamente una clase java que haga de web service, pero para generar luego el cliente nos viene bien tener una interfaz, así que hacemos una interfaz java que será nuestro web service. Una simple Calculadora con el método suma
package com.chuidiang.ejemplos.cxf; import javax.jws.WebService; @WebService public interface Calculadora { double suma(double a, double b); }
Lo único "extraño" que hemos hecho aquí es añadir la anotación @WebService. Esto permite más adelante construir el Web Service o el Cliente a partir de aquí.
El servidor[editar]
En el lado del servidor, sólo debemos hacer una clase CalculadoraImpl que implemente esa interfaz y que haga algo en los métodos correspondientes
package com.chuidiang.ejemplos.cxf; public class CalculadoraImpl implements Calculadora { public double suma(double a, double b) { return a + b; } }
Aquí no hemos puesto el @WebService, puesto que ya lo tiene la interfaz Calculadora y no lo necesitamos. Si lo ponemos tampoco estorba.
Y ahora sólo nos queda publicar esta clase como Web Service. El main() que lo hace es muy sencillo
package com.chuidiang.ejemplos.cxf; import javax.xml.ws.Endpoint; import org.apache.cxf.jaxws.EndpointImpl; public class Main { public static void main(String[] args) { Endpoint endpointCalculadora = EndpointImpl.create(new CalculadoraImpl()); endpointCalculadora.publish("http://localhost:8080/Calculadora"); } }
Simplemente creamos un Endpoint pasándole una instancia de nuestra implementación de Calculadora. Luego publicamos con el método publish() indicando cual es la URL con la que queremos que esté accesible el Web Service. Arrancando el programa podremos visitar http://localhost:8080/Calculadora?wsdl y ahí veremos el fichero WSDL, definido automáticamente a partir de nuestra clase. Si esta URL funciona y nos muestra el fichero WSDL (un XML raro), es que nuestro Web Service está funcionando.
Es posible al arrancar que te salgan errores de versiones de jaxb y jax-ws. Echa un ojo al último apartado para ver cómo evitarlos.
EndPoint es lo suficientemente listo como para permitirnos publicar varios servicios en la misma URL de base (aunque con distinto nombre). Si tuvieramos también una clase HolaMundo, podríamos poner
package com.chuidiang.ejemplos.cxf; import javax.xml.ws.Endpoint; import org.apache.cxf.jaxws.EndpointImpl; public class Main { public static void main(String[] args) { Endpoint endpointCalculadora = EndpointImpl.create(new CalculadoraImpl()); endpointCalculadora.publish("http://localhost:8080/Calculadora"); Endpoint endpointHolaMundo = EndpointImpl.create(new HolaMundoImpl()); endpointHolaMundo.publish("http://localhost:8080/HolaMundo"); } }
y no habría conflicto con el http://localhost:8080. En Calculadora tendríamos nuestra clase CalculadoraImpl, y en HolaMundo nuestra clase HolaMundoImpl.
El Cliente[editar]
A partir de la interfaz, podemos crear un cliente java para llamar al Web Service y ver que funciona. El código sería este
package com.chuidiang.ejemplos.cxf; import java.net.MalformedURLException; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; public class Cliente { public static void main(String[] args) throws MalformedURLException { URL urlWsdlCalculadora = new URL("http://localhost:8080/Calculadora?wsdl"); QName nombreServicioCalculadora = new QName( "http://cxf.ejemplos.chuidiang.com/", "CalculadoraImplService"); Service servicioCalculadora = Service.create(urlWsdlCalculadora, nombreServicioCalculadora); Calculadora calculadora = servicioCalculadora.getPort(Calculadora.class); System.out.println(calculadora.suma(11.0, 12.0)); } }
Con las dos primeras líneas indicamos dónde está el WSDL del Web Service y cómo se llama dicho Web Service. La ubicación del WSDL ya la vimos antes.
El QName (Qualified Name) consta de un namespace y de un nombre. Podemos ver ambos dentro del WSDL
<wsdl:definitions xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://cxf.ejemplos.chuidiang.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="CalculadoraImplService" targetNamespace="http://cxf.ejemplos.chuidiang.com/"> ....
Son los tags name y targetNamespace, así que nos basta copiarlos de ahí. En cualquier caso, el targetNamespace es el paquete de la clase que hemos convertido en web service pero puesto en forma de URL y el nombre es el nombre de la clase con la palabra Service detrás.
Volviendo al código del cliente, ahora llamamos a Service.create() pasando la url y el QName.
Service servicioCalculadora = Service.create(urlWsdlCalculadora, nombreServicioCalculadora);
Una vez creado el servicio, llamando a getPort() pasándole la interfaz del Web Service (Calculadora.class), nos devuelve una instancia de esa interfaz que hará las veces de cliente.
Calculadora calculadora = servicioCalculadora.getPort(Calculadora.class);
Y esto es todo, a partir de ahora cada vez que llamemos a un método de calculadora, realmente se estará invocando al método correspondiente del Web Service, la operación se realizará en el lado del servidor y obtendremos los resultados en el lado del cliente.
System.out.println(calculadora.suma(11.0, 12.0));
Puedes ver todo el código en chuidiang-ejemplos
Posible error[editar]
Java 1.6 lleva dentro clases de la especificación de Web Services. Puedes encontrarte el problema de que la librería que uses de web services (CXF en este caso) no sea compatible con esas clases que vienen con Java. Sabrás que te está pasando esto si al arrancar el Web Service o el cliente te sale un error de este estilo
Exception in thread "main" java.lang.LinkageError: JAXB 2.0 API is being loaded from the bootstrap classloader, but this RI (from jar:file:/D:/work/jaxws-ri/lib/jaxb-impl.jar! /com/sun/xml/bind/v2/model/impl/ModelBuilder.class) needs 2.1 API. Use the endorsed directory mechanism to place jaxb-api.jar
La forma de evitar este problema es poner en el directorio endorsed de java las librerías con una API más actualizada. Puedes ver los detalles en web-services-cxf-errores-de-version-con-jaxb-api
Siguiente paso[editar]
El siguiente paso sería hacer lo mismo, pero desplegando el web service sobre un servidor tomcat.