El API JAXM
Introducción
El API Java para Mensajes XML (JAXM) proporciona una forma
estándard para enviar mensajes sobre Internet desde la plataforma Java. Está
basado en las especificaciones SOAP 1.1 y SOAP con Attachments y puede extenderse
para trabajar con protocolos de alto nivel como ebXML o
BizTalk.
Para poder hacer mensajería JAXM, un negocio usa un servicio
de proveedor de mensajería, que hace el trabajo detrás de la escena requerido para
transportar y enrutar los mensajes. El proveedor de mensajería implementa el API
JAXM, de forma similar a como un driver para una base de datos
implementa el API JDBC. Todos los mensajes
JAXM van a través del proveedor de mensajería, por eso cuando un
negocio envía un mensaje, el mensaje primero va a los emisores del proveedor de
mensajería, luego a los recipientes del proveedor de mensajería, y finalmente al
recipiente destino. También es posible enrutar un mensaje para que vaya directamente
a los recipientes, llamados actors, antes de ír a su último
destino.
Como todos los mensajes van a través de él, un proveedor de mensajería puede tener
cuidado de los detalles como la asignación de identificadores de mensajes y de seguir
la pista de si un mensaje había sido enviado antes. Un proveedor de mensajería también
puede intentar reenviar un mensaje que no ha alcanzado su destino en el primer
intento de envío. Lo bonito de un proveedor de mensajería es que el cliente que usa la
tecnología JAXM ("cliente JAXM") está totalmente despreocupado
de lo que el proveedor está haciendo en segundo plano. El cliente JAXM simplemente hace
llamadas a métodos de JAXM, y el proveedor de mensajería,
trabajando con el contenedor, si existe uno, hace que todo suceda.
Aunque no es necesario, la mensajería JAXM normalmente tiene lugar
dentro de un contenedor, generalmente un servlet o un
contenedor J2EE. Una de las ventajas de estar en un contenedor es que podemos tener
oyentes, lo que hace posible recibir mensajes de forma asíncrona. El oyente recibe el
mensaje como una operación, y el recipiente envía una respuesta como una operación
subsecuente, lo que hace la mensajería asíncrona. Cuando no hay oyentes, los mensajes
se envían mediante un método que se bloquea hasta que obtiene una respuesta. Esta
clase de mensajería es síncrona, lo que significa que enviar un mensaje y recibir una
respuesta son una operación contínua y que nada sucede hasta que la operación se haya
completado.
Un mensaje JAXM se compone de dos partes, un parte SOAP obligatoria
y una parte attachment opcional. La parte SOAP, que consta de un objeto
SOAPEnvelope que contiene un objeto
SOAPHeader y un objeto
SOAPBody, puede contener un documento XML como el
contenido de el mensaje que está siendo enviado. Si queremos enviar varios documentos o
contenidos que no sean un documento XML, nuestro mensaje necesitará contener una parte de
attachment. No hay limitación en el contenido de la parte attachment, por eso podemos
enviar imágenes o cualquier otra clase de contenido.
Crear un Mensaje
Obtener una Conexión a un Proveedor de Mensajería
Lo primero que un cliente JAXM debe hacer es obtener una conexión a su proveedor
de mensajería. Usamos la conexión para crear un objeto
MessageFactory, que luego puede usarse para crear
objetos Message. Una vez que se ha rellenado el
mensaje, se usará de nuevo la conexión para envíar el mensaje.
El siguiente código demuestra la obtención de una factoría de mensajes y su uso para
crear una conexión. Las primeras dos líneas usan el API JNDI para recuperar el
objeto ConnectionFactory apropiado desde el servicio
de nombres donde fue registrado con el nombre "CoffeeBreakProvider". Cuando este nombre
lógico se pasa como un argumento, el método lookup
devuelve el objeto ConnectionFactory al que se unió el
nombre lógico. El valor devuelto es un Object Java, que
debe ser dirigido a un objeto ConnectionFactory para que
se pueda utilizar para crear una conexión. La tercera línea usa un método
JAXM para obtener realmente la conexión:
Context ctx = getInitialContext();
ConnectionFactory cf = (ConnectionFactory)ctx.lookup("CoffeeBreakProvider");
Connection con = cf.getConnection();
El ejemplar Connection con
representa una conexión al proveedor de mensajería "The Coffee Breaks". En las
siguientes líneas de código, se usa para crear un objeto
MessageFactory, que luego es usado para crear un objeto
Message:
MessageFactory messageFactory = con.getMessageFactory();
Message m = messageFactory.createMessage();
Parte de la flexibilidad del API JAXM es que permite especificar un
uso para una cabecera SOAP. Por ejemplo, los protocolos ebXML o
BizTalk pueden construirse sobre mensajería SOAP. Este uso de SOAP
mediante uno grupo de estándars o industriales dado se llama un "perfil" (profile). Si se
da el método getMessageFactory sin argumentos, como se hizo
en el fragmento de código anterior, devolverá un objeto
MessageFactory que produce un mensaje que usa el perfil base
SOAP. Así, el objeto Message m
creado en la línea de codigo anterior soportará un mensaje SOAP básico. Si se le pasa un
perfil al método getMessageFactory, los objetos
Message creados por el objeto
MessageFactory resultante soportarán el perfil especificado.
Por ejemplo, en el siguiente fragmento de código, m2
soportará el perfil que se suministró a getMessageFactory:
MessageFactory messageFactory2 = con.getMessageFactory(<profile>);
Message m2 = messageFactory2.createMessage();
Cada uno de los nuevos objetos Message
m y m2 contienen
automáticamente el objeto SOAPPart requerido, pero sus
cabeceras no tienen ningún contenido todavía. Las siguientes secciones ilustran las
formas típicas para añadir contenido.
Rellenar un Mensaje
Hay dos formas para añadir contenido a un mensaje:
- Pasar un objeto javax.xml.transform.Source al objeto
SOAPEnvelope. El objeto
Source puede ser un
SAXSource, un DOMSource,
o un StreamSource. Este objeto contiene el contexto para
el mensaje y también la información necesaria para actuar como una fuente de entrada. Un
objeto StreamSource contendrá el contenido como un
documento XML; los objetos SAXSource o
DOMSource contendrán contenido e instrucciones para
transformarlo en un documento XML.
- Crear elementos separados conteniendo el contenido y añadiéndolos individualmente. En
este caso, construimos un document XML, usando objetos String,
COMMENT, y CDATA según sea
necesario.
Rellenar la Parte SOAP de un Mensaje
Como se explicó antes, todos los mensajes creados con un objeto
SOAPPart, que tiene un objeto
SOAPEnvelope contienen un objeto
SOAPHeader y otro SOAPBody.
Una forma de añadir contenido a la parte SOAP de un mensaje es crear un objeto
SOAPHeaderElement o un
SOAPBodyElement y añadir un documento XML que hemos
construido con objetos String,
COMMENT, y CDATA. Otra forma
es añadir contenido al objeto SOAPEnvelope pasándole un
objeto javax.xml.transform.Source, que podría ser un objeto
SAXSource, un DOMSource, o un
StreamSource.
El siguiente fragmento de código ilustra cómo añadir contenido como un objeto
StreamSource. El primer paso siempre es obtener el objeto
SOAPPart desde el objeto
Message y usarlo para obtener un objeto
SOAPEnvelope:
SOAPPart soapPart = m.getSOAPPart();
SOAPEnvelope soapEnvelope = soapPart.getSOAPEnvelope();
Luego el código construye el documento XML a añadir. Usa un objeto
DocumentBuilderFactory para obtener un objeto
DocumentBuilder. Luego analiza el fichero dado para
producir el documento que se usará para inicializar el objeto
newDOMSource. Finalmente, el código pasa el objeto
DOMSource domSource al
objeto SOAPEnvelope:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse("file:///foo.bar/soap.xml");
DOMSource domSource = new DOMSource(doc);
soapEnvelope.setContent(domsrc);
Rellenar la Parte de Attachment de un Mensaje
Un objeto Message podría no tener parte attachment,
pero si tiene algo que no sea un documento XML, debe tener una parte attachment.
Podría haber cualquier número de partes attachment, y podrían contener cualquier cosa,
desde texto plano (incluyendo documento XML) a ficheros de imágenes. En el siguiente
fragmento de código, el contenido es una imagen en un fichero JPEG, cuya URL se usa para
inicializar el objeto javax.activation.DataHandler
dh. El objeto Message
m crea el objeto
AttachmentPart attachPart,
que es inicilizado con el DataHandler que contiene la
URL de la imagen. Finalmente, el mensaje se añade
attachPart a sí mismo:
URL url = new URL("http://foo.bar/img.jpg");
DataHandler dh = new DataHandler(url);
AttachmentPart attachPart = m.createAttachmentPart(dh);
m.addAttachmentPart(attachPart);
Enviar un Mensaje
Una vez que hemos rellenado el objeto Message, estamos
preparados para enviarlo usando un objeto
Connection (una conexión a nuestro proveedor de
mensajería). Si la mensajería es asíncrona, es decir, se hace en el contexto de un
contenedor como un Servlet o un contenedor J2EE, se envía usando el método
send. Si la aplicación que envía el mensaje no se está
ejecutando en un contenedor, lo que significa que la mensajería es síncrona, se usa el
método call. Los parámetros para ámbos métodos son (1)
el objeto Message que está siendo enviado y (2) el objeto
Endpoint que representa el destino al que se está enviando.
El siguiente fragmento de código envia el mensaje asíncronamente porque usa el meodo
send. Crea un objeto Endpoint
endPoint desde una URI para el
recipiente deseado y luego pasa los objetos endPoint y
m al método send:
Endpoint endPoint = new Endpoint("http://foo.bar/Service");
con.send(m, endPoint);
Si nuestra aplicación no se está ejecutando en un contenedor, necesitamos usar el método
call para enviar el mensaje. Este método toma los mismos
parámetros que el método send, pero, al contraro que él,
se bloquea hasta que recibe una respuesta, como se muestra en la siguiente línea de
código:
Message reply = con.call(m, endPoint);