Creación de un WebService desde Cero con CXF y Maven

Introducción

En la presente guía construiremos un Web Service básico desde cero, utilizando Maven y un framework open source para la construcción de servicios: Apache CXF.

CXF se para sobre el estándar de Java para Web Services: JAX-WS, que, a su vez, hace uso de otros estándares, como, por ejemplo, JAXB para el proceso de Binding (la asociación entre una clase Java y su correspondiente definición en el XML de los mensajes a transmitir).

Como siempre, podemos utilizar directamente JAX-WS para nuestros servicios web (como también podríamos usar JDBC si quisiéramos acceder a una base de datos en la forma más básica, o la especificación de Servlets/JSP para crear aplicaciones web), sin embargo CXF nos facilita varias cuestiones, y está integrado con otros Frameworks, como Spring, que mencionaremos en apartados posteriores, y que aceleran procesos tediosos, como la publicación y la generación de clientes.

Para realizar la guía necesitaremos una instalación activa de Maven en nuestra máquina, emplearemos Eclipse como IDE y Apache Tomcat, como Web Server.

A su vez, se sugiere tener instalado el plugin de Maven: m2Eclipse, ya que facilitará muchas de las acciones que llevemos a cabo por medio de esta herramienta.

Construcción

El primer paso, como siempre, es la construcción del proyecto básico.

Para ello emplearemos un archetype, que, como vimos, nos automatiza el proceso de construcción y nos agrega las dependencias que necesitemos, brindándonos una estructura estándar.

El archetype que emplearemos se denomina cxf-jaxws-javafirst.

Si usamos el plugin de Eclipse, podremos simplemente hacer New->Maven Project, y en la pantalla de selección del archetype, elegir el mencionado anteriormente:

Luego de esperar a que Maven contruya los directorios y archivos correspondientes, terminaremos obteniendo la estructura standard, y además un ejemplo, con un Web Service básico, y listo para utilizar, llamado HelloWorld.

El árbol de dependencias de nuestro nuevo proyecto es el siguiente:

Nuestro proyecto cuenta dependencias propias del framework CXF, y otras de terceros, como la de commons-logging.

En la figura anterior, resaltamos con amarillo las dependencias de Spring [link], que es un framework que ofrece facilidades para varias cuestiones comunes a muchos proyectos. En nuestro caso, utilizaremos una de ellas, la Inyección de Dependencias, para facilitar la publicación de los servicios en forma declarativa a través de un simple archivo de configuración.

Finalizando el proceso de construcción, vale aclarar que si desde Eclipse recibimos un error de compilación en las clases del Servicio de ejemplo, indicando inconvenientes con la librería rt.jar, deberemos añadir un cambio en nuestras preferencias del proyecto.

Para ello, iremos a las propiedades del mismo, y en Item Java Compiler->Errors/Warnings, seleccionamos Enable Project Specific Settings.

En la parte inferior, tendremos que cambiar la opción “Deprecated and restricted API” de “Error” a “Warning” o “Ignore”:

Al terminar, nuestro proyecto quedará de esta forma:

Desarrollo y Publicación de un Servicio

Como comentamos anteriormente, el archetype seleccionado, no sólo nos construye la estructura básica del proyecto, sino que además nos da un Web Service de ejemplo llamado HelloWorld.

Este ejemplo nos permitirá comprender la estructura básica que requiere un servicio web con el framework CXF.

A continuación analizaremos cada artefacto que revista alguna importancia:

Interface HelloWorld: Es lo que se conoce como SEI (Service Endpoint Interface). Aquí exponemos los métodos que proporcionará nuestro servicio.

Sólo tenemos que tener en cuenta la annotation @WebService, colocada sobre la definición de la interface para que el framework la reconozca como una SEI.

Clase HelloWorldImpl: A este tipo de clase se la conoce como SIB (Service Implementation Bean), y es la clase a través de la cual llevaremos a cabo la implementación de cada método que proporcione el servicio. Como muestra el ejemplo, necesitamos implementar el SEI, y colocar el fully qualified name de la clase en la annotation @WebService.

Como vemos, para poder desarrollar un web service sólo necesitamos estas dos clases. Esta tendencia de simplificar la cantidad de artefactos necesarios para crear servicios se ha vuelto habitual en Java, como se puede observar en la especificación de EJB 3.0. Anteriormente se requerían más clases para lograrlo, volviendo el proceso de desarrollo sumamente tedioso.

Adicionalmente debemos prestar atención principalmente a dos archivos de configuración:

Web.xml: Aquí, como en toda aplicación web, podemos definir nuestras preferencias respecto a filtros, seguridad etc.

En este archivo es importante la definición del servlet CXFServlet. Este Servlet recibirá las peticiones, y de acuerdo a las URLs, instanciará los servicio correspondiente, basándose en la información del archivo beans.xml.

Beans.xml: Es el archivo del que se valdrá el framework, para, a través de Spring, instanciar los servicios correspondientes. En el caso del ejemplo HelloWorld, podemos observar el id del servicio, la URL del mismo, y la clase concreta a instanciar:

<jaxws:endpoint

id="helloWorld"

implementor="com.utn.tacsWS.HelloWorldImpl"

address="/HelloWorld" />

Podemos tener varios servicios coexistiendo del mismo proyecto, y para publicar cada uno de ellos debemos colocar una información análoga a la que proporcionamos para el ejemplo anterior, dentro de un nuevo endpoint.

Este archivo también nos permite setear parámetros adicionales, dentro de cada endpoint, por ejemplo, si deseamos utilizar alguna característica especial del framework, como su capacidad para la transferencia eficiente de archivos, a través del protocolo MTOM:

<jaxws:endpoint ...>

<jaxws:properties>

<entry key="mtom-enabled" value="true" />

</jaxws:properties>

</jaxws:endpoint>

Deploy

Una vez que tenemos creado el proyecto, desarrollado y configurado el servicio, para desplegarlo en un web server, deberemos generar un war, como en toda aplicación web.

Como sabemos, con Maven esto es muy sencillo. Si contamos con el plugin de Eclipse, sólo debemos hacer click derecho sobre el proyecto->Run As->Generate WAR. Esto es posible ya que en el POM.XML, especificamos el tipo de packaging:

<packaging>war</packaging>

Podemos lograr el mismo efecto por línea de comandos, a través del comando mvn package.

El resultado será, como siempre, un war que se encontrará en el directorio “target”. En nuestro caso, obtendremos el artefacto tacsWS-0.0.1-SNAPSHOT.war.

Para finalizar el deploy, deberemos copiar el WAR obtenido, en el directorio “webapp” del Tomcat. Cuando iniciamos, Tomcat realizará el deploy automáticamente del war.

En general, es aconsejable reducir el nombre del artefacto obtenido al artifactId del proyecto, en nuestro caso se llamaría tacsWS.war, no es necesario mantener el nombre completo que nos da el proceso de package.

Si levantamos el Tomcat, veremos que contamos con una nueva aplicación web.

Por último para ver el WSDL del web service desplegado, podemos dirigirnos a la dirección desde cualquier browser.

http://IP:TOMCAT_PORT/tacsWS/HelloWorld?wsdl.

El WSDL es un archivo que contiene un conjunto de definiciones que describen un Web Service. Proporciona toda la información necesaria para accederlo y utilizarlo. Indica qué hace el servicio, cómo se comunica y dónde reside. En pocas palabras estipula el contrato de nuestro servicio.

Con esto finaliza la creación de un Web Service básico. A continuación complicaremos el ejemplo un poco más, modificando la interfaz del método para que reciba un objeto más complejo.

Recibiendo objetos por parámetro

Para la comunicación, todo web service utiliza mensajes basados en XML. En particular, se hace uso de un estándar: SOAP (Simple Object Access Protocol).

SOAP es un protocolo que constituye la base para todo Web Service, proporciona un mecanismo simple para permitir a una aplicación enviar un XML a otra. Es un protocolo de alto nivel que define solamente la forma de intercambio de los mensajes y algunas reglas para el procesamiento de los mismos. Es completamente independiente del protocolo que realice el transporte, es decir, puede ser transmitido a través de HTTP, JMS, o cualquier otro.

Desde nuestra aplicación, programaremos utilizando clases, como siempre lo hacemos en objetos. El mapping entre las clases y definiciones xml lo realizará el framework de forma automática, basándose en estándares y convenciones. Sin embargo, en aquellos casos en que no nos alcance el default podemos indicar parámetros de configuración adicionales a través de annotations, lo que se conoce como mapping strategy:

Esto es similar a lo que ocurría con los ORM, pero en lugar de mapear clases a tablas, estaremos mapeando clases a XML Types.

Avanzando en el ejemplo, si quisiéramos que el método en lugar de recibir tipos básicos como Strings, Integers, etc, recibiera un objeto, sólo tendríamos que agregar a la clase en cuestión la annotation:

@javax.xml.bind.annotation.XmlType

Sobre la definición de la clase.

Con esto indicamos que la clase debe mapearse a un XML Type (de la misma forma que hacíamos en Hibernate para indicar que una clase es persitente, a través de la annotation @Entity).

Algunas de las annotations básicas a tener en cuenta para realizar los mapeos son:

@WebMethod: Como su nombre lo indica esta annotation acompaña la definición de los métodos del WebService.

@WebParam: Para extender la metadata sobre los parámetros.