miércoles, 8 de octubre de 2014

Uso de Jax-WS Handlers para leer encabezados (headers) en los Servicios Web

La semana pasada un compañero de trabajo me comento que no encontraba como leer un encabezado de un mensaje soap mediante el uso de web-services. He decidido compartir esta información para que sea más accesible de encontrar en la web. La respuesta es fácil... usa handlers.

Jax-WS Handlers

El framework JAX-WS (el último API de Java usado para la creación de servicios web basados en SOAP: para publicar y consumir servicios Web) también proporciona un framework Handlers.

Los Handlers (controladores) proporcionan un medio para inspeccionar y manipular los mensajes SOAP entrantes o salientes (tanto en el cliente como en el servidor). Ellos actúan como poderosos interceptores de mensajes que pueden realizar una variedad de funciones tales como la transformación de mensajes, filtrado de contenidos, seguimiento (tracking), etc. De hecho, los handlers se utilizan a menudo en entornos de ejecución para implementar las especificaciones de SOAP y servicios web tales como WS-Security, WS-ReliableMessaging, etc.

Los JAX-WS Handlers son similares a los interceptores EJB o filtros de servlets. Tanto los Handlers, como los interceptores y los filtros, recomiendan a los desarrolladores a seguir la cadena del patrón de responsabilidad.

Tipos de JAX-WS Handlers

JAX-WS ofrece dos tipos de handlers (con dos interfaces): LogicalHandler y SOAPHandler.

  • LogicalHandlers son el protocolo de mensajes neutral. 
  • LogicalHandlers sólo tienen acceso a la carga útil del mensaje de un mensaje. 
  • SOAPHandlers también se conocen como controladores de mensajes o de protocolo. 
  • SOAPHandlers tienen acceso a todo el mensaje SOAP. 
nos enfocaremos en los SoapHandlers.

SOAPHandlers 

La interfaz SOAPHandler requiere que la clase del Handler implemente cuatro métodos:
handleMessage, handleFault, close y getHeaders. 

  • El handleMessage suele ser el método de mayor interés en un handler. El método handleMessage es invocado para el procesamiento normal por el framework como lo es el envío o recepción del mensaje (entrante y saliente). 
  • El método handleFault es invocado por el framework cuando se produce un error de SOAP durante el procesamiento de mensajes. 
  • El método close es invocado por el framework justo antes del envío del mensaje. El método de close permite cualquier limpieza o liberación de recursos al final del procesamiento del handler.

A continuación se muestra un ejemplo de un SOAPHandler (menos el método getHeaders que se explicará a continuación) que se encarga de contar el número de elementos en el encabezado (header) y el cuerpo (body) de un mensaje SOAP. 

//required imports here

public class HeaderHandler implements SOAPHandler {

    @Override 
    public void close(MessageContext context) { 
    }

    @Override 
    public boolean handleFault(SOAPMessageContext context) { 
        return false; 
    }

    @Override 
    public boolean handleMessage(SOAPMessageContext messagecontext) { 
        Boolean outbound = (Boolean) messagecontext 
                .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); 
        if (outbound) { 
            System.out.println("SOAP message departing…"); 
        } else { 
            System.out.println("SOAP message incoming…"); 
        } 
        try { 
            SOAPMessage message = messagecontext.getMessage(); 
            SOAPHeader header = message.getSOAPHeader(); 
            if (header != null) { 
                Iterator i = header.getChildElements(); 
                System.out.println("Number of header elements:  " 
                        + countElements(i)); 
            } 
            SOAPBody body = message.getSOAPBody(); 
            if (body != null) { 
                Iterator i = body.getChildElements(); 
                System.out.println("Number of body elements:  " 
                        + countElements(i)); 
            } 
        } catch (Exception e) { 
            System.out.println(e); 
            e.printStackTrace(); 
        } 
        return true; 
    }

    private int countElements(Iterator i) { 
        int count = 0; 
        while (i.hasNext()) { 
            count++; 
            i.next(); 
        } 
        return count; 
    }
}

El último método, getHeaders, es justo el punto de interés de esta entrada del blog. El método getHeaders recupera los bloques de la cabecera que pueden ser procesados por el handler. No toma ningún parámetro y devuelve un conjunto de nombres completos de encabezado.

El método GetHeaders()

El método getHeaders() obtiene el encabezado soap  (header:soap) así como el tiempo de ejecución del servicio de hosting (alojamiento), de los cuales el handler es el encargado de procesar. Devuelve los QNames del elemento exterior de cada encabezado SOAP que el handler entiende. 

MustUnderstand Not Understood! 


Si has trabajado con los mensajes SOAP, seguramente te habrás dado cuenta que los elementos de header SOAP pueden venir con un atributo global SOAP llamado: mustUnderstand. Los valores válidos para mustUnderstand son true | false (SOAP 1.2) ó 1 | 0 (SOAP 1.1). Este atributo es utilizado para indicar si se requiere o no el receptor o intermediario de servicios web  para entender el elemento de la cabecera antes de procesar el mensaje. 

El SOAPHandler puede servir como un "intermediario" que procesa el encabezado SOAP antes de que el servicio real se encargue de procesar el resto (Body) del mensaje. 

Por ejemplo, a continuación un ejemplo de mensaje SOAP que contiene falsas credenciales de seguridad para ser usado por el lado del servidor para autenticar/autorizar alguna invocación del servicio. Observa donde esta el atributo mustUnderstand entre nombre de usuario (usernameHeader) y contraseña (passwordHeader) de los elementos que tiene la cabecera? 

 
    
      Jim 
      supersecret 
    
    
       
         4 
       
    


si algo no procesa los elementos de la cabecera: nombre de usuario y contraseña, este mensaje podría retornar una falla al cliente. Por ejemplo, supongamos que el servicio no maneja los elementos de la cabecera y el metodo getHeaders del SOAPHandler se codifica como se muestra a continuación.


@Override 
    public Set getHeaders() { 
        return null; 
    }


Dado que nada maneja el usuario y contraseña de la cabecera, el mensaje SOAP resultante dirigido al cliente incluiría un código de fallo MustUnderstand como se muestra aquí

 
    
       
         soap:MustUnderstand 
         MustUnderstand headers: [{urn:com.intertech.secty}password, {urn:com.intertech.secty}username] are not understood. 
       
    


Usando getHeaders() para entender el MustUnderstand


Sin embargo, el método getHeaders () puede ser codificado para indicar al entorno de ejecución que el handler de SOAP se va a encargar de los elementos de la cabecera mustUnderstand devolviendo un conjunto de objetos QName (nombre completo) que coinciden con los elementos de la cabecera mustUnderstand. A continuación, un ejemplo con el nuevo método getHeaders () que maneja el usuario y contraseña de las cabeceras.

@Override 
public Set getHeaders() { 
    System.out.println("Inside SOAP handler of get Headers"); 
    QName securityUsernameHeader = new QName("urn:com.intertech.secty", 
            "username"); 
    QName securityPasswordHeader = new QName("urn:com.intertech.secty", 
            "password"); 
    HashSet headers = new HashSet(); 
    headers.add(securityUsernameHeader); 
    headers.add(securityPasswordHeader); 
    System.out.println("got Headers:  " + headers); 
    return headers; 
}

Ahora, si el mismo elemento SOAP con los elementos de la cabecera: nombre de usuario y contraseña se envía a un servicio, mediante el HeaderHandler del ejemplo, no se genera un fallo. Se supone por el tiempo de ejecución que los elementos de la cabecera fueron consumidos por el handler. Por supuesto, en un mundo real para el SOAP handler, en el método handleMessage () deberías agregar algún código para manejar efectivamente estos encabezados.

Espero que esta información pueda serles de gran utilidad.


No hay comentarios:

Publicar un comentario