Modificar un mensaje SOAP desde Java
El propósito del siguiente artículo, es explicar el modo en que se puede modificar un mensaje SOAP en una llamada a un Web Service desde NetBeans. Esto es muy útil en aquellos casos en que consumimos un WebService generado en otro lenguaje de programación, y no encontramos la forma de acceder a algún objeto que se encuentra dentro del mensaje.Imaginemos que tenemos el siguiente mensaje SOAP para la llamada a un WebService:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<cabeceraSOAP xmlns="http://switchoffandletsgo.blogspot.com/">
<ipOrigen>string</ipOrigen>
</cabeceraSOAP>
</soap:Header>
<soap:Body>
<SumaNumeros xmlns="http://switchoffandletsgo.blogspot.com/">
<intNum1>int</intNum1>
<intNum2>int</intNum2>
</SumaNumeros >
</soap:Body>
</soap:Envelope>
Se puede observar que el WebService tiene un método SumaNumeros que recibe dos enteros y devolvería la suma de ambos. Además contiene una cabecera SOAP en la que se debe enviar la dirección IP de origen.
Al generar las clases necesarias para realizar la llamada utilizando el WSDL del WebService, NetBeans no es capaz de reconocer la cabecera del mensaje SOAP, con lo que no podemos establecer directamente la variable cabeceraSOAP. Por lo tanto, tendremos que agregar un Handler que intervenga antes de realizar la llamada, y añada al mensaje la cabecera que necesitemos.
Para ello creamos una nueva clase, por ejemplo SumaNumerosHandler y pedimos que implemente la clase SOAPHandler<SOAPMessageContext>:
public class SumaNumerosHandler implements SOAPHandler<SOAPMessageContext> {
}
Después le pedimos a NetBeans que realice los imports necesarios y que implemente los métodos abstractos, con lo que nos aparecerá la definición de los métodos que tenemos que implementar:
public Set<QName> getHeaders() {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean handleMessage(SOAPMessageContext arg0) {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean handleFault(SOAPMessageContext arg0) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void close(MessageContext arg0) {
throw new UnsupportedOperationException("Not supported yet.");
}
En concreto vamos a centrarnos en el método handleMessage, que se ocupará de capturar la salida o entrada del mensaje SOAP. Para el resto de métodos que no nos ocupan, vamos a definir una serie de valores de retorno por defecto:
public Set<QName> getHeaders() {
return Collections.EMPTY_SET;
}
public boolean handleFault(SOAPMessageContext messageContext) {
return true;
}
public void close(MessageContext context) {
}
Método handleMessage
Como se puede observar, este método recibe un parámetro SOAPMessageContext. Esta interface da acceso al mensaje SOAP en cada petición o respuesta que se realiza en la comunicación, pudiendo modificar el contenido de este mensaje.
Para obtener el mensaje, llamamos al método getMessage():
SOAPMessage msg = messageContext.getMessage();
También necesitaremos conocer la dirección en la que viaja el mensaje, esto es, si se trata de un mensaje de salida o de entrada. En nuestro caso, necesitaremos modificar únicamente el mensaje de salida (petición). Podemos obtener esta información mediante el método get(), tal como se ilustra en el siguiente fragmento de código:
boolean bolMsgSalida= (Boolean) messageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
Por último, para añadir la cabecera a nuestro mensaje de salida, vamos a hacer uso de una serie de objetos que nos permitirán editar y modificar el mensaje SOAP:
// Obtenemos el contenedor del mensaje SOAP
SOAPPart sp = msg.getSOAPPart();
// A partir del contendor, obtenemos el nodo "Envelope"SOAPEnvelope env = sp.getEnvelope();
// Instanciamos un objeto SOAPFactory para crear cualquier elemento perteneciente a un mensaje SOAP, en nuestro caso, los nodos que formarán la cabeceraSOAPFactory soapFactory = SOAPFactory.newInstance();
// Definimos los elementos a incluir en el mensajeSOAPElement soapElementoCabecera = soapFactory.createElement("cabeceraSOAP","","http://switchoffandletsgo.blogspot.com/");
SOAPElement soapIpOrigen= soapFactory.createElement("ipOrigen","", "http://switchoffandletsgo.blogspot.com/");
// Rellenamos la información del nodo ipOrigensoapIpOrigen.addTextNode("192.168.0.1");
// Incluimos los elementos dentro de los objetos correspondientessoapElementoCabecera.addChildElement(soapIpOrigen);
SOAPHeader soapHeader = env.addHeader(); // Crea un elemento cabecera SOAP
soapHeader.addChildElement(soapElementoCabecera);
Si no se ha producido ninguna excepción, el método devolverá true.
Este manejador de mensajes SOAP, debería generar el siguiente nodo dentro del mensaje que se enviará al servidor:
<soap:Header>Finalmente, debemos asignar el nuevo manejador al Web Service que queremos consumir. Para ello, accederemos a la carpeta "Web Service References" de nuestro proyecto de NetBeans, y pulsaremos con el botón derecho sobre el Web Service que queremos asignar. En el menú desplegable aparecerá la opción "Configure Handlers...".
<cabeceraSOAP xmlns="http://switchoffandletsgo.blogspot.com/">
<ipOrigen>1921.168.0.1</ipOrigen>
</cabeceraSOAP>
</soap:Header>
Únicamente tendremos que añadir "Add..." nuestra clase SumaNumerosHandler y aceptar.
A partir de este ejemplo, las opciones son infinitas, podemos modificar cualquier parte del mensaje SOAP, entrante o saliente, según sean nuestras necesidades. En todo caso, siempre que sea posible, la mejor opción reside en utilizar las clases y métodos que genera NetBeans a partir del WSDL del Web Service.
12 comentarios:
Hola, el articulo, en verdad es muy bueno, gracias por compartir esta informacion. Sabes, pude modificar el SOAP de salida de un servicio web, pero no he podido acceder al SOAP de entrada. ¿como obtengo el mensaje de entrada? tengo que modificar la propiedad context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); ya intente de todo, pero no he podido, ojala y puedas ayudarme, es muy importante para mi. gracias.
La variable bolMsgSalida tendrá valor true cuando el mensaje es de salida y false cuando es de entrada.
El método es el mismo, simplemente controla con un if si bolMsgSalida = false para poder tratar el mensaje de entrada.
Ya me contarás...
Te agradesco mucho que hallas contestado mi mensaje. Sabes, tengo lo siguiente:
public boolean handleMessage(SOAPMessageContext context) {
try{SOAPMessage g =context.getMessage();
boolean ban=(Boolean)context.get(context.MESSAGE_OUTBOUND_PROPERTY);
if(ban==true){
SOAPPart sp = g.getSOAPPart();
SOAPEnvelope env = sp.getEnvelope();
env.setAttribute("elias","Mensaje salida");}
else{
SOAPPart sp = g.getSOAPPart();
SOAPEnvelope env = sp.getEnvelope();
env.setAttribute("elias","Mensaje entrada");
}
return true;
}catch (Exception e) {
throw new UnsupportedOperationException("Not supported yet ¿todavia?");
}
}
Lo que trato de Hacer es añadirle un atributo al Envelope (tanto en la salida como en la entrada). En el mensaje de salida si se añade el atributo al Envelope, pero en el de entrada no. Gracias por tu atension.
para controlar sì es un mensaje de salida, o de entrada, no basta con preguntar si esta en true o en false la variable ounbound, ya intente de todo, y creo que no es posible
Hola Elias, estoy montando un pequeño ejemplo, en cuanto lo tenga probado lo publico.
Voy un poco mal de tiempo, así que igual tardo un poco...
Hola, con la buena noticia de que ya pude modificar el mensaje soap tanto de entrada, como de salida, pero todo gracias a las bases que expusiste en tu ejemplo, te agradesco muchisimo, gracias
Me alegro mucho que lo hayas solucionado. Lamento no haber podido contestar antes, pero me queda muy poco tiempo al día para dedicarle a estas cosas, se puede observar por el descenso en el número de artículos publicados...
Hola
muy buen articulo, lo primero.
Te comento he creado un web service con su manejador para modificar el mensaje soap de entrada . Quiero que envien unos datos en la cabecera y quiero modificarlos yo antes que se procesen
por ejemplo añadiendo la ip del llamante
Boolean outboundProperty = (Boolean) smc.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
HttpServletRequest req = (HttpServletRequest) smc.get("javax.xml.ws.servlet.request");
out.println("\ndireccion?:"+ req.getRemoteAddr());
obtengo la ip
lo que no se como se puede modificar una cabecera que ya existe
Gracias
hola muy bueno tu tutorial yo tengo un problema con un header y no lo e podido solucionar... te muestro la cabecera
None
UsrSIEBEL
pwdSIEBEL
es muy importante para mi parametrisar los datos de usuario y contraseña para poder conectarme con el servicio te agredesco mucho me ayudes
me dio un dolo de cabeza el no encontrar información adecuada,
Como me ayudo demasiado esta explicación, Muchas Gracias !!!
Hola, soy nueva en esto de los servicios web y ahora estoy buscando como poder firmar los mensajes xml, pero ya que el xml se forma solo cuando yo creo un wewb service client en netbeans, no sé como puedo tomar ese xml para aplicarle las funciones indicadas para firmar, me imagino que tiene que ser desde una clase pero como detecto yo ese xml y lo firmo antes de que se envíe al proveedor, alguien que me pueda ayudar?
Publicar un comentario