Manipulando mensajes SOAP en Java con CXF: Una guía práctica 💻

¡Hola a todos! En el mundo de los servicios web, a menudo necesitamos interactuar con los mensajes SOAP de forma programática. A veces, la lógica de negocio requiere que inspeccionemos o modifiquemos las cabeceras (headers) o el cuerpo del mensaje antes de que la solicitud llegue al servicio. Una de las mejores maneras de lograr esto, especialmente con Apache CXF y Spring, es utilizando un Handler (manejador).

En este artículo, te mostraré cómo implementar un Handler para interceptar y manipular mensajes SOAP, basándome en un ejemplo funcional y bien estructurado.


Paso 1: Crea tu clase HandlerChain

Lo primero que haremos es crear una clase Java que implemente la interfaz SOAPHandler. Este es el componente central que interceptará los mensajes.

import javax.xml.namespace.QName;
import javax.xml.soap.*;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.soap.SOAPFaultException;
import java.util.Set;

public class HandlerChain implements SOAPHandler<SOAPMessageContext> {

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        System.out.println("Server: handleMessage()......");

        Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

        // Se ejecuta solo para mensajes de respuesta (inbound)
        if (!isRequest) {
            try {
                SOAPMessage soapMsg = context.getMessage();
                SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
                SOAPHeader soapHeader = soapEnv.getHeader();
                System.out.println("Msg header: " + soapMsg.getSOAPHeader());

                // Si no hay cabecera, agrega una y lanza una excepción
                if (soapHeader == null) {
                    soapHeader = soapEnv.addHeader();
                    generateSOAPErrMessage(soapMsg, "No SOAP header.");
                }

            } catch (SOAPException e) {
                System.err.println(e);
            }
        }
        return true;
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        System.out.println("Server: handleFault()......");
        return true;
    }

    @Override
    public void close(MessageContext context) {
        System.out.println("Server: close()......");
    }

    @Override
    public Set<QName> getHeaders() {
        System.out.println("Server: getHeaders()......");
        return null;
    }

    private void generateSOAPErrMessage(SOAPMessage msg, String reason) {
        try {
            SOAPBody soapBody = msg.getSOAPPart().getEnvelope().getBody();
            SOAPFault soapFault = soapBody.addFault();
            soapFault.setFaultString(reason);
            throw new SOAPFaultException(soapFault);
        } catch (SOAPException e) {
            System.err.println(e);
        }
    }
}

Análisis del código:

  • handleMessage: Este es el método más importante. Es llamado tanto para los mensajes de entrada (inbound) como para los de salida (outbound). El booleano isRequest nos ayuda a distinguir entre los dos. Aquí, el código se enfoca en los mensajes de respuesta (!isRequest), imprimiendo la cabecera y lanzando un error si no existe.

  • handleFault: Se activa si ocurre un error en el mensaje.

  • close: Se llama cuando se cierra el Handler.


Paso 2: Define el HandlerChain en un XML

Para que CXF sepa qué Handler usar, necesitamos un archivo de configuración XML. Este archivo define una cadena de Handlers y la clase que implementamos en el paso anterior.

Crea un archivo llamado handler-chain.xml y agrega el siguiente contenido, asegurándote de reemplazar class_path con la ruta completa a tu clase HandlerChain.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<javaee:handler-chains
    xmlns:javaee="http://java.sun.com/xml/ns/javaee"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <javaee:handler-chain>
        <javaee:handler>
            <javaee:handler-class>class_path.HandlerChain</javaee:handler-class>
        </javaee:handler>
    </javaee:handler-chain>
</javaee:handler-chains>

Paso 3: Agrega la anotación @HandlerChain a tu servicio

Finalmente, conecta el archivo de configuración a tu servicio web. En la clase que implementa tu servicio (Web Service), agrega la anotación @HandlerChain con la ruta al archivo XML que creaste.

@HandlerChain(file="/handler-chain.xml")
// ... resto de la implementación del Web Service

Con esto, cada vez que un mensaje SOAP sea procesado por tu servicio web, pasará a través de tu clase HandlerChain, donde podrás inspeccionar, modificar o rechazar el mensaje según tus necesidades.

Para agregar una cabecera SOAP, puedes usar el siguiente código, tanto en el cliente como en el servidor, dependiendo de si quieres añadirlo a la solicitud o a la respuesta.

// Agrega una cabecera SOAP en el lado del servidor
List<Header> headers = new ArrayList<>();
Header dummyHeader = new Header(new QName("uri:org.apache.cxf", "dummy"), "decapitated",
                                new JAXBDataBinding(String.class));
headers.add(dummyHeader);
context.getMessageContext().put(Header.HEADER_LIST, headers);

Este método es poderoso y muy flexible, y te permite tener un control total sobre el procesamiento de tus mensajes SOAP.

¿Qué otros trucos de servicios web has descubierto? ¡Déjanos un comentario y comparte tu experiencia!

About Mario | Macla Editorial

Especialista en tecnología y ciberseguridad corporativa. Consultor en estrategias de transformación digital, automatización con IA y análisis de infraestructura. A través de MaclaTech, asesoro a organizaciones en la implementación de soluciones tecnológicas seguras y escalables para el mercado global.

0 comments:

Publicar un comentario