Servicios web con Apache CXF (utilizando JAX-WS y JAX-RS)

Creo me extendí un poco en la redacción de éste apunte así que podrían comenzar a leer desde la sección de Servicios web en Java. Este es mi primer aporte, son bienvenidas las quejas y sugerencias.

Antecedentes generales...

Un servicio web es un mecanismo o método para intercambiar información entre aplicaciones. Normalmente los encontramos expresados como servicios de un sistema que pueden ser consumidos por clientes de distintas plataformas, lenguajes, etc.
Normalmente hacemos uso de estos servicios en una configuración base específica: el formato de intercambio es XML y el mecanismo de transferencia es HTTP, aunque también podemos utilizar otros mecanismos de transferencia como FTP, SMTP, etc.
Para poder definir qué servicios se encuentran expuestos en una dirección específica, la estructura de la información de entrada y salida entre otros aspectos, es necesario hacer uso de otros mecanismos que normalmente se expresan mediante “contratos” o documentos de definición del servicio WSDL.

De ésta forma ya conocemos qué mecanismos o servicios se encuentran disponibles, la información que necesitamos enviar (y su formato), el formato de la respuesta, así como también la dirección mediante la cual podemos acceder a éste servicio.

Ya que se necesita estandarizar la forma en la que describimos todo lo anterior, es necesario apegarse a una versión del documento. Generalmente ésto depende de la configuración del motor que expone el servicio, ya sea del servidor de aplicaciones o la tecnología utilizada. Aquí es donde entran los formatos de mensaje o protocolos de intercambio SOAP, XML-RPC, los cuales contienen la estructura que debe de tener el mensaje para ser procesado o para admitir el procesamiento de la solicitud.

Pero bueno, para todo eso pueden consultar la wikipedia o documentarse más en otras fuentes. Lo importante es que en muy raras ocasiones nos percatamos de todo ésto por que hacemos uso de “ayudantes” o mecanismos que nos permiten definir en un nivel más alto la interacción y exposición de los servicios.

Desarrollo alrededor de los servicios web

Normalmente existen dos acercamientos al desarrollo de servicios web, aquellos basados en definir primero el contrato (contract-first) o aquellos que definen primero el código y luego se expone el servicio generando automáticamente el contrato.

Las ventajas y desventajas se pueden resumir en lo siguiente: Cuando existe una gran cantidad de sistemas que hacen uso de un servicio, la modificación en el código, la migración de servidores o motores de webservices, pueden derivar en la generación automática del contrato (y su modificación con respecto al anterior) provocando que también tengan que ajustarse los clientes. En tanto que al definir el contrato primero y respetando la estructura, cualquier modificación al código no representará riesgo alguno a los clientes.

En este “apunte” no se mostrará el enfoque Contract-First, sin embargo se les invita a darle una revisión más detallada. La documentación de Spring les puede ayudar a realizar pruebas utilizando Contract-First.

Servicios Web en Java

En Java existe JAX-WS que nos permite exponer funcionalidades de nuestras clases como servicios web mediante el uso de anotaciones, de esta forma es posible que desde el código definamos qué métodos o funciones serán publicados, exponer los objetos de respuesta(*), entre otros.

NOTA: A mi punto de vista es una muy mala práctica el exponer objetos de respuesta como un XML en texto plano (es decir una sola cadena de texto) dejando al desarrollador la responsabilidad de procesar el XML devuelto.

Archivo de Interfaz

package com.test;

import javax.jws.WebService;

@WebService
public interface Saludo {
    String sayHi(String text);
}

Archivo de Implementación

package com.test;

import javax.jws.WebService;

@WebService(endpointInterface = "com.test.Saludo")
public class SaludoImpl implements Saludo {
       
    public String sayHi(String text) {
        System.out.println("sayHi called");
        return "Saludos mandandos a llamar << 'i;ió  >> " + text;
    }
}

Generalmente, ésto es lo único que hace falta para exponer un servicio y publicarlo en un contendor de aplicaciones que soporte JAX-WS. Automáticamente el contenedor se encargará de generar el contrato (WSDL).

Existe también JAX-RS lo cual permite realizar lo mismo pero exponiendolo como recursos REST

Implementación incluyendo anotaciones JAX-RS

package com.test;

import javax.jws.WebService;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

@WebService(endpointInterface = "com.test.Saludo")
@Produces("application/json")
public class SaludoImpl implements Saludo {
       
        @GET
        @Path("/{a}")
    public String sayHi(@PathParam("a")String text) {
        System.out.println("sayHi called");
        return "Saludos mandandos a llamar << 'i;ió  >> " + text;
    }
}

Iniciando con CXF

CXF es un proyecto de la fundación Apache que permite definir de una manera específica gran parte de las especificaciones o mecanismos que queremos utlizar para exponer nuestros servicios, soportando:

WSDL Bindings: MTOM, PureXML, SOAP 1.1, SOAP 1.2
Frontends: Simple, Clientes dinámicos, Anotaciones, JAX-WS/JAX-RS
DataBindings: MTOM, SDO, XMLBeans,JAXB, Aegis
Transportes: Local, JMS, HTTP, Camel

De esta forma podemos hacer uso o de la configuración básica o especificar los motores o mecanismos que serán utilizados para o ajustar nuestros servicios a algunos ya existentes o bien a no dejar ésta tarea al servidor de aplicaciones (y a los caprichos de los fabricantes de los mismos).

Otra de las ventajas que nos proporciona CXF es que podemos controlar cuerpo del mensaje a lo largo de toda la cadena de procesamiento del mismo es decir, podemos manipular aún la respuesta en el instante previo al que sea enviado al cliente (esto puede servir cuando quieran por ejemplo hacer que el texto de una cierta sección venga encapsulado dentro de un bloque CDATA).

La práctica

En general, CXF permite incrustar el motor de servicios dentro de una aplicación web (permitiendo utilizar un contenedor de aplicaciones simple), minimizando configuración adicional, generación de instancias de componentes, etc y para ello hace uso de Spring; por lo que es necesario agregarlo al web.xml de la aplicación web.

--INICIA Paréntesis
Para los que no estén acostumbrados a utilizar Spring, solo bastará con decirles que al iniciar la aplicación web, se genera una instancia de una clase que se encargará de crear los objetos descritos en un archivo de configuración y que en tiempo de ejecución serán utilizados, relacionados, inyectados en otras clases que lo soliciten (y se encuentren en el archivo de configuración).

Realmente es recomendable que le den una revisada rápida a Spring.
--TERMINA Paréntesis

El archivo web.xml deberá tener además el siguiente contenido.

<context-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>WEB-INF/appCtx.xml</param-value>
        </context-param>

        <listener>
                <listener-class>
                        org.springframework.web.context.ContextLoaderListener
                </listener-class>
        </listener>

        <servlet>
                <servlet-name>CXFServlet</servlet-name>
                <servlet-class>
                        org.apache.cxf.transport.servlet.CXFServlet
                </servlet-class>
                <load-on-startup>1</load-on-startup>
        </servlet>

        <servlet-mapping>
                <servlet-name>CXFServlet</servlet-name>
                <url-pattern>/*</url-pattern>
        </servlet-mapping>

En el servlet mapping podemos definir la URL base desde la cual las llamadas realizadas al servidor serán procesadas por el servlet de CXF.

En el archivo de configuración de Spring será necesario:
Agregar las extensiones del esquema para jaxws (y en su caso jaxrs)
Definir los endpoints (URLs en donde se expondrán los servicios)
Definir los beans (o instancias de las clases) que serán utilizados.

Por lo que el archivo de configuración de Spring tendrá el siguiente contenido.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jaxws="http://cxf.apache.org/jaxws"
        xmlns:jaxrs="http://cxf.apache.org/jaxrs"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd"
>

        <import resource="classpath:META-INF/cxf/cxf.xml" />
        <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
        <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" />
        <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
       
        <!--Webservice-->
        <jaxws:endpoint
          id="helloWorld"
          implementor="#saludoBean"
          address="/wsSaludo" >
                  <jaxws:dataBinding>
                                <bean class="org.apache.cxf.jaxb.JAXBDataBinding">
                                </bean>
                  </jaxws:dataBinding>
                  <!--<jaxws:outInterceptors>
                     <bean class="com.test.Interceptor"/>
                  </jaxws:outInterceptors>-->
          </jaxws:endpoint>
         
          <!--REST Service-->
  <jaxrs:server id="saludoService" address="/restSaludo">
    <jaxrs:serviceBeans>
      <ref bean="saludoBean" />
    </jaxrs:serviceBeans>
  </jaxrs:server>

  <bean id="saludoBean" class="com.test.SaludoImpl" />

</beans>

De la sección siguiente se expresa que se generará un servicio en la URL /wsSaludo que hará uso del bean (instancia) “saludoBean”. La sección dataBinding permite definir el motor que se utlizará para convertir un objeto en XML (en caso que estén haciendo uso de un motor distinto como Aegis). Por el momento no es necesario agregar esa sección.

<jaxws:endpoint
          id="helloWorld"
          implementor="#saludoBean"
          address="/wsSaludo" >
                  <jaxws:dataBinding>
                                <bean class="org.apache.cxf.jaxb.JAXBDataBinding">
                                </bean>
                  </jaxws:dataBinding>
                  <!--<jaxws:outInterceptors>
                     <bean class="com.test.Interceptor"/>
                  </jaxws:outInterceptors>-->
          </jaxws:endpoint>

La sección de configuración del servicio REST, indica la URL en la que se expondrá y el bean que utiliza para proveer el servicio.

  <jaxrs:server id="saludoService" address="/restSaludo">
    <jaxrs:serviceBeans>
      <ref bean="saludoBean" />
    </jaxrs:serviceBeans>
  </jaxrs:server>

Al iniciar la apliación web (incluso en un contendor simple como tomcat o jetty) y acceder a la URL en la que configuraron el servlet de CXF, encontrarán el listado de los servicios expuestos y que son manejados por el motor de CXF :

Available SOAP services:
Saludo
sayHi
Endpoint address: http://localhost:8080/proyecto/wsSaludo
WSDL : {http://test.com/}SaludoImplService
Target namespace: http://test.com/

Available RESTful services:
Endpoint address: http://localhost:8080/proyecto/restSaludo
WADL : http://localhost:8080/proyecto/restSaludo?_wadl&type=xml

Basta decir que para realizar pruebas sobre esos servicios pueden o generar sus propios clientes, o utilizar alguna aplicación para probar servicios web como SOAP UI.

Referencias u otras lecturas

http://cxf.apache.org/
http://cxf.apache.org/docs/cxf-architecture.html (Este enlace es genial, pues puede darles un panorama más amplio sobre el proyecto CXF)
http://josearrarte.com/blog/2009/06/14/web-services-con-spring-framework...

Código del proyecto

El enlace al proyecto es el siguiente: http://dl.dropbox.com/u/18342172/CXF.zip

UPDATE: Una disculpa, el POM contenido en el proyecto no funcionaba correctamente, ahora ya está corregido y para generar el war sólo es necesario utilizar el comando mvn package. El war lo despliegan en cualquier contenedor. Saludos.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Aclaración sobre publicación del servicio REST.

Es importante notar que los servicios REST mantienen otra filosofía con respecto a las operaciones.

(Como dice la wikipedia) mientras en el enfoque orientado a llamadas a procedimientos remotos se centra en las operaciones disponibles, como agregaUsuario, agregaPost, eliminaComentario, etc... en REST se enfocan más a los recursos: USUARIOS, POSTS, COMENTARIOS y delegan las operaciones sobre éstos a los métodos de solicitud definidos en el protocolo HTTP (GET, POST, PUT, DELETE...).

Por lo que ésto solo es una prueba de concepto.