Java SOAP API

(Imagen: Rembrandt - Abraham e Isaac)

Puede sernos útil en algún momento el generar y procesar mensajes SOAP. Veamos un poco del SOAP API, mediante una aplicación que simula enviar un mensaje SOAP como request y recibir otro como response:

package ch01.soap;

import java.util.Date;
import java.util.Iterator;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPPart;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.Node;
import javax.xml.soap.Name;

public class DemoSoap{
private static final String LocalName = "TimeRequest";
private static final String Namespace = "http://ch01/mysoap/";
private static final String NamespacePrefix = "ms";

private ByteArrayOutputStream out;
private ByteArrayInputStream in;

private static void main(){
new DemoSoap().request();
}

private void request(){
try{
SOAPMessage msg = create_soap_message();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
Name lookup_name = create_qname(msg);
hdr.addHeaderElement(lookup_name).addTextNode("time_request");
out = new ByteArrayOutputStream();
msg.writeTo(out);
trace("The sent SOAP message: ", msg);
SOAPMessage response = process_request();
extract_contents_and_print(response);

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

private SOAPMessage create_soap_message(){
SOAPMessage msg = null;
try{
MessageFactory mf = MessageFactory.newInstance();
msg = mf.createMessage();
}
catch (SOAPException e){System.err.println(e);}
return msg;

}

private SOAPMessage create_soap_message(InputStrean in){
SOAPMessage msg = null;
try{
MessageFactory mf = MessageFactory.newInstance();
msg = mf.createMessage(null, in);

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

}

private Name create_qname (SOAPMessage msg){
Name name = null;
try{
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
name = env.createName(LocalName, NamespacePrefix, Namespace);
}
catch (SOAPException e){System.err.println(e);}
return name;

}

private void trace(String s, SOAPMessage m){
System.out.println("\n");
System.out.println(s);
try{
m.writeTo(System.out);
}
catch (SOAPException e){System.err.println(e);}
catch (IOException e){System.err.println(e);}
}

private SOAPMessage process_request(){
process_incoming_soap();
coordinate_streams();
return create_soap_message(in);
}

private void process_incoming_soap(){
try{
coordinate_streams();
SOAPMessage msg = create_soap_message(in);
Name lookup_name = create_qname(msg);
SOAPHeader = msg.getSOAPHeader();
Iterator it = header.getChlidElements(lookup_name);
Node next = (Node)it.next();
String value = (next==null)?"Error!": next.getValue();
if (value.toLowerCase().contains("time_request")){
String now = new Date().toString();
SOAPBody body = msg.getSOAPBody();
body.addBodyElement(lookup_name).addTextNode(now);
msg.saveChanges();
msg.writeTo(out);
trace("The received/processed SOAP message: ", msg);
}
}
catch (SOAPException e){System.err.println(e);}
catch (IOException e){System.err.println(e);}

}

private void coordinate_streams(){
in = new ByteArrayInputStream(out.toByteArray());
out.reset();
}

private void extract_contents_and_print(SOAPMessage msg){
try{
SOAPBody body = msg.getSOAPBody();
Name lookup_name = create_qname(name);
Iterator it = body.getChildElements (lookup_name);
Node next = (Node) it.next();
String value = (next==null)? "Error!": next.getValue();
System.out.println("\n\nReturned from server: " + value);
}
catch (SOAPException e){System.err.println(e);}
}
}

Entonces, esta sencillísima y breve aplicación genera un mensaje SOAP y añade la cadena time_request al header del SOAP Envelope. Eso lo hace en las líneas:


SOAPMessage msg = create_soap_message();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
Name lookup_name = create_qname(msg);
hdr.addHeaderElement(lookup_name).addTextNode("time_request");
Existen dos maneras de crear un mensaje SOAP. La primera:



MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();


Y la segunda:


SOAPMessage msg = mf.createMessage(mime_headers, input_stream);

En la que el primer argumento es una colección de headers de la capa de transporte (como los headers HTTP) y el segundo es un inputStream con los bytes para crear el mensaje. Una vez que el mensaje es creado, se extraen las cabeceras del SOAP Envelope y se inserta un nodo de texto XML con el valor de time_request. El mensaje SOAP resultante sería:


<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<ms:TimeRequest xmlns:ms="http://ch01/mysoap/">
time_request
</ms:TimeRequest>
</SOAP-ENV:Header>
<SOAP-ENV:Body/>
</SOAP-ENV:Envelope>


El cuerpo del mensaje (SOAP body) siempre es obligatorio, pero puede estar vacío. La SOAP header es opcional, pero en este caso contiene el texto time_request.

EL método request envía el mensaje SOAP a través de un ByteArrayOutputStream, para simular el enviarlo a través de la red a otro host. El método request invoca al método process_request, que a su vez delega el procesamiento a otros métodos. En fin, el mensaje SOAP es creado de un ByteArrayInputStream,dado que este stream contiene el mensaje SOAP. Así:



SOAPMessage msg = null;
try {
MessageFactory mf = MessageFactory.newInstance();
msg = mf.createMessage(null, in);
}


Y luego procesamos este mensaje SOAP para extraer la cadena time_request. La extracción se hace así: se extrae la SOAP Header del mensaje SOAP y se itera sobre los elementos con el nombre de tag:

Una vez encontrado, se busca que contenga la cadena time_request con la siguiente lógica:


SOAPHeader header = msg.getSOAPHeader();
Iterator it = header.getChildElements(lookup_name);
Node next = (Node) it.next();
String value = (next == null) ? "Error!" : next.getValue();



Si la SOAP header contiene la cadena solicitada, se extrae el SOAP Body del mensaje SOAP recibido, y se le añade un elemento conteniendo la hora actual. El mensaje SOAP modificado es enviado como respuesta. Así:


if (value.toLowerCase().contains("time_request")) {
String now = new Date().toString();
SOAPBody body = msg.getSOAPBody();
body.addBodyElement(lookup_name).addTextNode(now);
msg.saveChanges();

msg.writeTo(out);
trace("The received/processed SOAP message:", msg);
}


El mensaje SOAP enviado sería el siguiente:


<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<ms:TimeRequest xmlns:ms="http://ch01/mysoap/">
time_request
</ms:TimeRequest>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ms:TimeRequest xmlns:ms="http://ch01/mysoap/">
Mon Oct 27 14:45:53 CDT 2008
</ms:TimeRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


Basado en el capítulo 1 de Java Web Services: Up and Running de Martin Kalin

Publicar un comentario