SAAJ: Sin condiciones

En el momento de redactar este documento, la mayoría de los servicios web consisten en simples intercambios de mensajes: un cliente se pone en contacto con un servicio web y envía un mensaje a ese servicio. El servicio web, a su vez, procesa esa solicitud y luego envía una respuesta al cliente. Ese patrón simple de solicitud / respuesta modela la forma en que el protocolo HTTP facilita las interacciones cliente / servidor web. Al igual que con HTTP, los intercambios de mensajes de servicios web a menudo deben incluir contenido binario, como imágenes, documentos o clips de sonido. Este artículo presenta el envío y la recepción de contenido de servicios web binarios mediante SOAP (Protocolo simple de acceso a objetos) con API de archivos adjuntos para Java (SAAJ) 1.2.

Antes de sumergirse en las complejidades de la transferencia de contenido de servicios web binarios, vale la pena señalar que un servicio web simple de solicitud / respuesta contrasta con los servicios que modelan la interacción cliente / servidor como llamadas a procedimientos remotos o RPC. En un RPC, un servidor expone una interfaz que se parece a una API. A su vez, un cliente invoca dicho servicio haciendo llamadas remotas en la API del servicio, pasando los parámetros requeridos y recibiendo los valores que produce la llamada.

El RPC basado en XML se parece a la forma en que se invocan objetos en un sistema orientado a objetos (OO). De hecho, cuando trabaja con la API de Java para RPC basado en XML (JAX-RPC), rara vez se da cuenta de que está trabajando con documentos XML, no con objetos Java. JAX-RPC le permite pensar en los servicios web como objetos remotos, como lo haría con Java RMI (Invocación de método remoto). El tiempo de ejecución de JAX-RPC traduce las llamadas al método OO de alto nivel a los documentos XML que espera el servicio web remoto. Si bien los servicios web de estilo RPC a menudo proporcionan un modelo de programación más conveniente, las llamadas RPC también deben depender de una capa de mensajería de nivel inferior para intercambiar los mensajes XML que componen la llamada remota.

Para algunos servicios web, a menudo es útil programar directamente en esa capa de mensajería de nivel inferior. Por ejemplo, si desea invocar un servicio web que consume un documento de orden de compra y devuelve un recibo, puede modelar fácilmente ese intercambio de documentos como un único intercambio de mensajes de solicitud / respuesta. En lugar de realizar invocaciones de métodos remotos, construiría mensajes XML, enviaría esos mensajes directamente a un servicio web y procesaría la respuesta XML del servicio, si existe. Dado que SOAP define el formato de mensaje común para los mensajes de servicio web, necesitaría construir mensajes que cumplan con SOAP y, una vez que el servicio responda, analizar esos mensajes de respuesta SOAP en un formato que su programa comprenda.

SAAJ proporciona una biblioteca conveniente para construir y leer mensajes SOAP y también le permite enviar y recibir mensajes SOAP a través de la red. SAAJ define el espacio de nombres javax.xml.soap. Las clases que residen en ese paquete inicialmente formaron parte de la API de Java para mensajería XML (JAXM), pero recientemente se separaron en su propia API. JAXM se basa en SAAJ para la construcción y manipulación de mensajes SOAP, y agrega confiabilidad de mensajes y otras características específicas de la mensajería XML. Mientras que SAAJ es un componente obligatorio de J2EE (Java 2 Platform, Enterprise Edition) 1.4, JAXM no lo es. Este artículo se centra en uno de los aspectos más útiles de SAAJ: la capacidad de adjuntar contenido binario a un mensaje SOAP.

Los beneficios de los accesorios

Mientras que el centro de diseño de SOAP se centra en encapsular documentos XML en un mensaje, la función de adjuntos de SOAP amplía un mensaje SOAP para incluir, además de la parte SOAP normal, cero o más adjuntos, como muestra la Figura 1. Cada adjunto está definido por un tipo MIME y puede asumir cualquier contenido representado como un flujo de bytes.

La función de adjuntar SOAP resulta más útil cuando un cliente desea transmitir datos binarios, como una imagen o datos de audio, a un servicio web. Sin archivos adjuntos SOAP, enviar una pieza de datos binarios resultaría más difícil. Por ejemplo, el mensaje SOAP de un cliente podría transmitir la dirección URL del archivo binario. El cliente entonces tendría que operar un servidor HTTP para permitir que el servicio web recupere ese archivo. Eso representaría una carga indebida para cualquier cliente de servicios web, especialmente para los clientes que se ejecutan en dispositivos de recursos limitados como cámaras digitales o escáneres. La capacidad de adjuntar SOAP permite que cualquier cliente de servicios web capaz de transmitir mensajes SOAP incruste archivos binarios directamente en un mensaje SOAP.

Los archivos adjuntos SOAP, por ejemplo, resultan útiles al interactuar con los sitios web del portal. Considere una red de agencias de bienes raíces que necesita distribuir descripciones y fotografías de casas en venta a un portal de búsqueda de bienes raíces centralizado. Si el portal opera un servlet que permite la publicación de mensajes SOAP con archivos adjuntos, una agencia inmobiliaria podría actualizar sus listados con algunos mensajes SOAP, incluidas fotos de esas casas. El cuerpo del mensaje SOAP puede incluir la descripción de la propiedad y los archivos adjuntos SOAP pueden contener los archivos de imagen. En ese escenario, cuando el servlet de un operador de portal recibe tal mensaje, devolvería un documento de reconocimiento, indicando la disponibilidad de la publicación en el portal. La Figura 2 ilustra un servicio web de este tipo.

La anatomía de SOAP con mensaje de archivos adjuntos

La Nota de mensajes SOAP con archivos adjuntos W3C (World Wide Web Consortium) (ver Recursos) no agrega nuevas funciones a SOAP. Más bien, define cómo aprovechar los tipos MIME en un mensaje SOAP para definir archivos adjuntos y cómo hacer referencia a esos archivos adjuntos desde el cuerpo de SOAP.

El tipo MIME multipart/relateddefine documentos que constan de múltiples partes relacionadas. Los mensajes SOAP con archivos adjuntos deben seguir el multipart/relatedtipo MIME. El siguiente ejemplo muestra un multipart/relatedmensaje SOAP, vinculado al protocolo HTTP, con dos archivos adjuntos:

POST / propertyListing HTTP / 1.1 Host: www.realproperties.com Tipo de contenido: Multiparte / Relacionado; boundary = MIME_boundary; tipo = texto / xml; Longitud del contenido: NNNN --MIME_boundary Tipo de contenido: texto / xml; charset = UTF-8 Codificación de transferencia de contenido: ID de contenido de 8 bits: Really Nice Homes, Inc. Agregar 1234 Main St Pleasantville CA 94323 250000 - MIME_boundary Tipo de contenido: image / jpeg Content-ID: .... DATOS JPEG ..... --MIME_boundary Content-Type: image / jpeg Content-ID: .... JPEG DATA ..... --MIME_boundary--

El mensaje de varias partes anterior comprende una serie de encabezados MIME y datos relacionados. En la raíz del documento se encuentra el cuerpo SOAP. Dado que el cuerpo de SOAP contiene solo datos XML, el tipo MIME de todo el mensaje es text/xml. Después del sobre SOAP hay dos archivos adjuntos, cada uno correspondiente a un archivo de imagen enviado junto con el mensaje.

Un ID de contenido identifica cada archivo adjunto. La nota del W3C permite que un ID de contenido o una ubicación de contenido hagan referencia a los archivos adjuntos, pero da preferencia al primero. Dichos ID de contenido actúan como referencias de identificador uniforme de recursos (URI) a los adjuntos; las reglas de codificación SOAP 1.1 definen cómo hacer referencia a un recurso en un mensaje SOAP a través de un URI que puede hacer referencia a cualquier contenido, no solo a XML (consulte la Sección 5 de SOAP 1.1 en Recursos). Un procesador SOAP resuelve esas referencias URI a medida que procesa el mensaje. Según el ejemplo anterior, el procesador SOAP asocia el elemento frontImagecon la sección de datos con Content ID [email protected]en el mensaje SOAP.

Crea y envía un mensaje SOAP con archivos adjuntos

SAAJ le permite crear y editar cualquier parte de un mensaje SOAP, incluidos los archivos adjuntos. La mayor parte de SAAJ se basa en clases e interfaces abstractas, de modo que cada proveedor puede implementar SAAJ en sus propios productos. La implementación de referencia de Sun Microsystems viene con Java Web Services Developer Pack (JWSDP).

Dado que los mensajes SOAP representan una forma especial de documentos XML, JAAS se basa en la API del Modelo de objetos de documento (DOM) para el procesamiento XML. La mayoría de los componentes de los mensajes SOAP descienden de la javax.xml.soap.Nodeinterfaz, que, a su vez, es una org.w3c.dom.Nodesubclase. Subclases de SAAJ Nodepara agregar construcciones específicas de SOAP. Por ejemplo, un especial Node, SOAPElementrepresenta un elemento de mensaje SOAP.

Un resultado directo de la dependencia de SAAJ en interfaces y clases abstractas es que logra la mayoría de las tareas relacionadas con SOAP a través de métodos de fábrica. Para conectar su aplicación con la API de SAAJ, primero cree un SOAPConnectiondesde un SOAPConnectionFactory. Para crear y editar mensajes SOAP, también puede inicializar a MessageFactoryy a SOAPFactory. MessageFactoryle permite crear mensajes SOAP y SOAPFactoryproporciona los métodos para crear partes individuales de un mensaje SOAP:

SOAPConnectionFactory spConFactory = SOAPConnectionFactory.newInstance (); SOAPConnection con = spConFactory.createConnection (); SOAPFactory soapFactory = SOAPFactory.newInstance ();

Con estas herramientas en su lugar, puede crear un mensaje SOAP que un cliente de una agencia de bienes raíces usaría para enviar una actualización de listado a un sitio web del portal.

SAAJ ofrece varias formas de crear un nuevo mensaje SOAP. El siguiente ejemplo muestra el método más simple que crea un mensaje SOAP vacío con un sobre y el encabezado y el cuerpo en ese sobre. Como no necesita un encabezado SOAP en este mensaje, puede eliminar ese elemento del mensaje:

Mensaje SOAPMessage = factory.createMessage (); Encabezado SOAPHeader = message.getSOAPHeader (); header.detachNode ();

Agregar la estructura XML al cuerpo del mensaje resulta sencillo:

SOAPBody body = message.getSOAPBody (); Name ListingElementName = soapFactory.createName ("propertyListing", "realProperty", "//schemas.realhouses.com/listingSubmission"); SOAPBodyElement ListingElement = body.addBodyElement (ListingElementName); Nombre attname = soapFactory.createName ("id"); ListingElement.addAttribute (attname, "property_1234"); SOAPElement ListingAgency = ListingElement.addChildElement ("ListingAgency"); ListingAgency.addTextNode ("Really Nice Homes, Inc"); SOAPElement ListingType = ListingElement.addChildElement ("ListingType"); ListingType.addTextNode ("agregar"); SOAPElement propertyAddress = ListingElement.addChildElement ("propertyAddress"); SOAPElement street = propertyAddress.addChildElement ("calle"); street.addTextNode ("1234 Main St "); SOAPElement ciudad = propertyAddress.addChildElement (" ciudad "); city.addTextNode (" Pleasantville "); SOAPElement estado = propertyAddress.addChildElement (" estado "); estado.addTextNode (" CA "); SOAPElement zip = propertyAddress.addChildElement ("zip"); zip.addTextNode ("94521"); SOAPElement listPrice = listElement.addChildElement ("listPrice"); listPrice.addTextNode ("25000");addChildElement ("listPrice"); listPrice.addTextNode ("25000");addChildElement ("listPrice"); listPrice.addTextNode ("25000");

Tenga en cuenta que agrega el ID único de la propiedad como un atributo del propertyListingelemento. Además, califica el propertyListingelemento con un QNamenombre compatible con el espacio de nombres.

Puede agregar archivos adjuntos al mensaje SOAP de varias formas. En este ejemplo, primero crea elementos para denotar las imágenes frontal e interior de la propiedad enumerada. Cada uno tiene un hrefatributo que designa el ID de contenido del archivo adjunto:

String frontImageID = "[email protected]"; SOAPElement frontImRef = ListingElement.addChildElement ("frontImage"); Nombre hrefAttName = soapFactory.createName ("href"); frontImRef.addAttribute (hrefAttName, frontImageID); String interiorID = "[email protected]"; SOAPElement interiorImRef = ListingElement.addChildElement ("interiorImage"); interiorImRef.addAttribute (hrefAttName, interiorID);

Para adjuntar fácilmente los archivos de imagen requeridos al mensaje, use un javax.activation.DataHandlerobjeto de JavaBeans Activation Framework. DataHandlerpuede detectar automáticamente el tipo de datos que se le ha pasado y, por lo tanto, puede asignar automáticamente el tipo de contenido MIME apropiado al archivo adjunto:

URL url = nueva URL ("archivo: ///export/files/pic1.jpg"); DataHandler dataHandler = nuevo DataHandler (url); AttachmentPart att = message.createAttachmentPart (dataHandler); att.setContentId (IDImagen frontal); message.addAttachmentPart (att);

Alternativamente, puede pasar un Object, junto con el tipo MIME correcto, a createAttachmentPart(). Ese método se parece al primero. Internamente, la implementación de SAAJ probablemente buscará un DataContentHandlerpara manejar el tipo MIME especificado. Si no puede encontrar un manejador adecuado, createAttachmentPart()lanzará un IllegalArgumentException:

URL url2 = nueva URL ("archivo: ///export/files/pic2.jpg"); Imagen im = Toolkit.getDefaultToolkit (). CreateImage (url2); AttachmentPart att2 = message.createAttachmentPart (im, "imagen / jpeg"); att2.setContentId (interiorID); message.addAttachmentPart (att2);