Interacción de WebSphere Process Server mediante interfaz API de servicios web y método de organización JAXB

La interfaz de programación de servicios web ofrece una manera delicada de construir aplicaciones de cliente que interactúen con los procesos de negocio y las tareas humanas de WebSphere® ProcessServer. No obstante, a diferencia de la interfaz Enterprise JavaBeans™ (EJB), la interfaz API de servicios web no soporta la Carga de artefactos remotos (Remote Artifact Loading - RAL), y se debe contar con esquemas que tengan el formato adecuado en el cliente. Una solución delicada y eficaz para esquemas grandes y complejos genera los esquemas de cliente requeridos a través de Java™Architecture for XML Binding (JAXB) en tiempo de desarrollo y luego organiza los datos de negocio en tiempo de ejecución.

Walter M. Jenny, Business Solution Architect, IBM

Walter Jenny es Business Solution Architect en el equipo Business Performance and Services Optimization (BPMO) y tiene más de 20 años de experiencia. Desde hace tres años, forma parte de IBM Software Group. Su experiencia profesional incluye integración de negocios y gestión de los procesos de negocios. Walter obtuvo una Maestría en Ciencias de la Computación, una Maestría en Administración de Empresas y un Doctorado en Administración de Empresas.



03-08-2011

Introducción

Además de la tradicional interfaz Enterprise JavaBeans (EJB), la interfaz API de Servicios de mensajería Java (Java Messaging Service - JMS), la interfaz REST, y la interfaz API de servicios web son agregados relativamente recientes. Se describen las ventajas y desventajas de estas interfaces API en comparación con las interfaces de programación para interactuar con procesos de negocio y tareas humanas.

La interfaz API de servicios web se introdujo en la Versión 6.0.2 de WebSphere Process Server y ofrece una amplia funcionalidad para construir aplicaciones de cliente que interactúen con procesos de negocios y tareas humanas. Específicamente, brinda:

  • La capacidad de comunicarse con cualquier entorno de tiempo real que soporte llamadas a servicios web, incluyendo entornos Microsoft® .NET.
  • Exposición de los puntos de invocación subyacentes como servicios web.
  • Mejor aislamiento entre cliente y servidor.
  • La oportunidad de potenciar los esquemas existentes en la industria y los poderosos tiempos de ejecución y herramientas XML.

Al usar la interfaz API de EJB, el cargador de artefactos remotos ofrece un mecanismo para cargar artefactos existentes en su aplicación desde un servidor remoto. Aloja artefactos instalados en el servidor, poniéndolos a disposición de clientes RAL, en la misma celda o en otras diferentes. Así el cliente puede consultar o cargar artefactos desde los servidores RAL.

Pero como la interfaz API de servicios web no soporta RAL, los esquemas para datos de entrada, datos de salida y variables deben estar disponibles en el formato adecuado en el cliente. En este contexto, Java Architecture for XML Binding (JAXB) brinda una manera conveniente de enlazar un esquema XML a una representación en código Java. Esto facilita a los desarrolladores la tarea de incorporar datos XML y procesar funciones en aplicaciones basadas en tecnología Java sin necesidad de saber mucho acerca de XML propiamente dicho.

Aunque este artículo no es un tutorial sobre JAXB, describe cómo el entorno de tiempo de ejecución y desarrollo JAXB simplifican el proceso de mapear una definición de esquema (schema definition - XSD) XML a Java. Este artículo también trata sobre las características de tiempo de ejecución requeridas para desarrollar una interfaz básica de usuario que se genere dinámicamente en tiempo de ejecución a través de tiempo de ejecución JAXB y reflejo Java.

Java Architecture for XML Binding (JAXB)

Los objetos del ámbito de negocios y sus relaciones estructurales se pueden representar bien a través de un esquema XML. JAXB introduce el concepto de databinding (enlace de datos), la correspondencia entre esquemas XML y clases Java.

El compilador de esquemas JAXB crea clases Java e interfaces según la estructura del esquema XML, generalmente en tiempo de desarrollo. En tiempo de ejecución, se usan las bibliotecas JAXB para ejecutar los procesos de organización y desorganización de datos.Marshalling es el proceso mediante el cual se transforma uno o más objetos Java en un documento XML, y unmarshalling es el proceso opuesto, es decir crear un objeto Java a partir de un documento XML, como se muestra en la Figura 1.

Figura 1. Elementos JAXB
Elementos JAXB

¿Descendente o ascendente?

Puede crear servicios web usando dos métodos: top-downdevelopment (desarrollo descendente) creando un servicio a partir de un archivo WSDL, y bottom-up service development (desarrollo de servicio ascendente) creando los artefactos XML requeridos a partir de las clases Java existentes. Aunque JAXB soporta ambos, se recomienda el enfoque descendente porque brinda una mayor facilidad de interoperabilidad en todas las plataformas, enlaces y lenguajes de programación.

Las clases Java anotadas se generan a partir de un esquema XML con el compilador de esquemas xjc. Este paso, que normalmente se ejecuta en tiempo de desarrollo, crea un conjunto de JavaBeans que se mapea a los elementos y los tipos definidos en el archivo XSD. Los documentos de la instancia XML luego se convierten a/de objetos Java mediante el uso de la interfaz API en tiempo de ejecución de enlace JAXB.

Las clases anotadas contienen toda la información necesaria para que el tiempo de ejecución JAXB analice el XML para realizar el proceso de marshalling y unmarshalling, y en un paso optativo, valide los documentos. Usted puede usar las clases resultantes conjuntamente con la interfaz API de Java para tecnología de servicios web XML (JAX-WS) y la interfaz API de servicios web de WebSphere Process Server.

La ventaja de JAXB es que permite a los desarrolladores Java acceder a los esquemas XML y procesarlos sin necesidad de comprender detalladamente los mecanismos subyacentes. Además, la poderosa personalización en la forma de mapear esquemas a Java (y viceversa) es particularmente útil cuando el esquema es complejo y cambia frecuentemente, ya que sincronizar las correspondientes definiciones Java puede llevar mucho tiempo y ser propenso a errores.

En secciones posteriores trataremos las actividades relacionadas con desarrollo y tiempo de ejecución. De todas formas, antes de entrar en detalle, analicemos brevemente los principales bloques de construcción.

Arquitectura básica

La Figura 2 bosqueja un escenario posible con un navegador que accede a la aplicación de cliente y al backend WebSphere Process Server implantado en dos servidores.

Figura 2. Principales bloques de construcción
Principales bloques de construcción

La lógica de cliente se comunica con el proceso a través de proxies, los cuales representan a las operaciones de servicio web de Business Flow Manager (Administrador de flujo de negocio) (BFMIFProxy.java) y de Human Task Manager (Administrador de tareas humanas) (HTMIFProxy.java) como interfaces Java. Se generan a través de los artefactos WSDL exportados, configurando el entorno de desarrollo.

La comunicación entre todos los servidores debe ser segura. Todas las solicitudes de servicios web deben contener un token de seguridad que representa una autenticación de usuario válido. Los mecanismos de seguridad soportados por la interfaz API de servicios web son user name token (token de nombre de usuario) y Lightweight Third Party Authentication (LTPA) (Autenticación ligera de terceros). Como requisito previo, la aplicación Web debe ser segura y requiere una autenticación explícita. Para obtener una descripción detallada de esta configuración, consulte la sección sobre seguridad en Web service API - J2EE client [Interfaz API de servicios web - cliente J2EE].

Adicionalmente, si la aplicación de cliente se ejecuta en un nodo lógico aparte, se puede establecer un inicio de sesión único entre estos dos servidores para garantizar que el token LTPA se ejecute con cada invocación. Consulte Information Center: Single sign-on [Centro de información: inicio de sesión único] para obtener más información.


Actividades de desarrollo

Las siguientes secciones describen los pasos que se necesitan en tiempo de desarrollo.

Configuración del entorno de desarrollo de servicios web

Usar la interfaz API de servicios web de WebSphere Process Server implica una serie de pasos, como copiar los artefactos clave, generar los clientes proxy y verificar la seguridad. Los proxies de servicios web serán los principales puntos de interacción para su aplicación, y se generan en WebSphere Integration Developer a partir de los artefactos WSDL exportados, como se muestra en la Figura 3.

Figura 3. Generación de proxies de servicios web
Generación de proxies de servicios web

Estos pasos se documentan en Developing client applications [Desarrollo de aplicaciones de cliente] y Web service API - J2EE client - Version 7.0 [Interfaz API de servicios web - Cliente J2EE - Versión 7.0]. Habiendo preparado el entorno de desarrollo básico, analicemos más minuciosamente el otro bloque de construcción principal, JAXB.

Esquema de ejemplo

Como ya comentamos, una buena práctica para crear un servicio web es el enfoque descendente, partiendo de lenguajes WSDL y de esquemas. Para este ejemplo, usamos un tipo de esquema que todos conocemos, pero no necesariamente uno de los más queridos: el esquema de impuestos. La Figura 4 muestra los principales tipos en el editor de esquemas de WebSphere Integration Developer.

Figura 4. Esquema XML de ejemplo
Esquema XML de ejemplo

Un esquema XML representa el modelo de los datos o de la información y por lo tanto es un artefacto clave en cualquier desarrollo de software. A menudo es producto de debates y análisis exhaustivos (y ocasionalmente entretenidos) efectuados con los principales involucrados en la solución. Debe permanecer estrechamente ligado al modelo de proceso y de dominio y a los casos de uso.

Durante las etapas iniciales de diseño y desarrollo, el esquema probablemente evolucione, y las respectivas clases Java deben reflejar estos cambios. Para simplificar este proceso de mapeo, use JAXB.

Además probablemente no tenga control directo sobre los esquemas porque son definidos por otros grupos o son generados por herramientas especiales, como WebSphere Business Modeler. En estos casos, tiene poco margen para adaptar los esquemas de manera que sean más amigables para el mapeo. Por ejemplo, que adopten una forma que genere mapeos claros y precisos a clases Java. JAXB brinda una poderosa personalización para que este proceso sea menos traumático.

Enlace del documento de esquemas

Las clases de enlaces JAXB se generan con la herramienta de línea de comandosxjc, o directamente en el entorno WebSphere Integration Developer. Para generar las respectivas clases Java, vaya a la perspectiva J2EE, haga clic con el botón derecho en documento de esquemas y seleccioneGenerate (Generar) > Java, como se muestra en la Figura 5.

Figura 5. Generación de las clases Java
Generación de las clases Java

En el siguiente asistente, elija JAXB Schema to Java Bean (Esquema JAXB a Java Bean) y presione Next (Siguiente). Aparece la pantalla "XSD to Java" (XSD a Java), como se muestra en la Figura 6.

Figura 6. Asistente XSD a Java
Asistente XSD a Java

El panel ofrece las siguientes opciones:

  1. Generate schema library (Generar biblioteca de esquemas) le permite personalizar el archivo de esquemas para hacer el mapeo del proyecto (en el panel siguiente).
  2. En la lista Target Java Container (Contenedor Java de destino), especifique el proyecto o la carpeta que contenga los beans Java generados.
  3. En el campo Target Package (Paquete de destino), ingrese un nombre para el paquete Java o acepte el valor predeterminado.
  4. Una opción importante es Binding Files (Archivos de enlace),que personaliza el enlace predeterminado entre un componente de esquema XML y su representación Java. Haciendo clic en Add (Agregar) en el panel Binding Files, puede seleccionar archivos de declaraciones de enlace externas. Los archivos de enlace tienen, por convención, una extensión .xjb y se crean usando cualquier editor de texto. El formato y el contenido de estos archivos se describen posteriormente.
  5. Haga clic en Finish (Finalizar).

El compilador de enlace JAXB transforma al esquema XML en un grupo de las respectivas clases Java que concuerdan con la estructura descripta en el esquema. Contienen toda la información necesaria para que el entorno de tiempo de ejecución JAXB analice y reconstruya la representación XML original. Esto simplifica notablemente el modelo de programación de datos y permite realizar la creación de instancias de objetos de manera simple y también el uso de métodos captadores y configuradores. No es necesario escribir código para convertir los datos entre el formato de transferencia XML y la aplicación Java.

Examinemos este mapeo más detalladamente investigando el tipo “Case” (Caso), como se muestra en la Figura 7.

Figura 7. El tipo Case XSD
El tipo Case XSD
Listado 1. La fuente Case XSD
                <xsd:complexType
                name= "Case" ><xsd:sequence><xsd:element
                maxOccurs= "1" minOccurs= "1" name="Number" 
                type="xsd:string" /><xsd:element
                maxOccurs= "1" minOccurs= "1" name="Amount" 
                type="xsd:decimal" /><xsd:element
                maxOccurs= "1" minOccurs= "1" name="Created" 
                type="xsd:date"/ ><xsd:element
                maxOccurs= "1" minOccurs= "1" name="Sequence"
                type="xsd:integer"/ ><xsd:element
                name="Status"><xsd:simpleType><xsd:restriction
                base="xsd:string"><xsd:enumeration
                value="Open"/><xsd:enumeration
                value="Closed"/><xsd:enumeration
                value="Forwarded"/></xsd:restriction></xsd:simpleType>
                </xsd:element><xsd:element
                name="Product"><xsd:simpleType><xsd:restriction
                base="xsd:string"><xsd:enumeration
                value="IncomeTax"/><xsd:enumeration
                value="BusinessTax"/></xsd:restriction>
                </xsd:simpleType></xsd:element><xsd:element
                name="Approved"type="xsd:boolean"
                ></xsd:element><xsd:element
                maxOccurs="1"minOccurs="1"name="Taxation"type=
                "bo:Taxation"/><xsd:element
                maxOccurs="1"minOccurs="0"name="Objection"type=
                "bo:Objection"/><xsd:element
                maxOccurs="unbounded"minOccurs="0"name="History"type="bo:Reference"
                /></xsd:sequence></xsd:complexType>

Losenlaces de tipo de datos predeterminadosdel esquema a clases Java trabajan mayormente según lo esperado y el esquema anterior en el Listado 1 genera el siguiente código Java (se eliminaron las anotaciones para una mayor legibilidad) que se muestra en el Listado 2.

Listado 2. La clase Java Case
publicclassCase {protectedString
                    number;protectedBigDecimal amount;protectedXMLGregorianCalendar
                    created;protectedBigInteger sequence;protectedString
                    status;protectedString product;protectedboolean
                    approved;protectedTaxation taxation;protectedObjection
                    objection;protectedList<Reference>history;// Getter and
                Setter … }

La mayoría de los tipos se mapean a tipos epónimos simples de Java. Las excepciones especiales son los tipos decimal y entero, se correlacionan a números con un rango no especificado de dígitos fraccionarios y dígitos totales, y las mejores correlaciones en Java sonjava.math.BigDecimalK,java.math.BigInteger, yjavax.xml.datatype.XMLGregorianCalendar, respectivamente.

Qué sucede con JAX-WS y JAXB

JAX-WS usa JAXB como su mecanismo de enlace y usted puede personalizar los enlaces como lo explicamos aquí. Además, también puede controlar el WSDL al mapeo Java a través de declaraciones de enlace. Usted puede personalizar casi todos los componentes WSDL que se pueden mapear a Java, como clase de interfaz de punto final del servicio, nombre de método, nombre de parámetro, clase de excepción, etc. Consulte JAX-WS WSDL Customization [Personalización JAX-WS WSDL] para obtener más información.

Aunque algunas aplicaciones que manejan cantidades monetarias necesitarán mucha precisión, tal vez usted desee usar una secuencia simple de números enteros que sea más fácil de administrar en su código. La manera más fácil de configurar esto es mediante cambios rotundos de los tipos XSD, por ejemplo, usando el tipo incorporado xsd:int, o introduciendo un nuevo xsd:simpleType con la restricción adecuada.

Otro requisito común es la capacidad de anular las reglas de enlace predeterminadas ya sea por requisitos para la definición de nombres (como nombres de paquetes) o por incompatibilidad en la definición de nombres, anulando el mapeo predeterminado enums para que sea seguro a nivel tipo o para agregar documentación. Inlinecustomization (Personalización en línea) es una forma de hacer esto.

Sin embargo, estos enfoques asumen que usted tiene control directo sobre el esquema. Si éste no fuera el caso, la personalización externa podría ser útil.

La ObjectFactory ofrece una forma conveniente de crear los objetos necesarios que componen los subelementos del árbol. Por diseño, un objeto JAXB recién creado es “shallow” (superficial), lo cual significa que si contiene tipos complejos en su estructura de árbol, estos objetos no se crean y son sólo null(nulos). Ni los métodos del elemento “object factory” ni los constructores de las clases Java le brindan un algoritmo estándar para la construcción de árbol. Esto aporta flexibilidad en la manera de construir el árbol, pero significa que es su responsabilidad construir el árbol. Volveremos a este tema en la sección Creating subtrees [Creación de subárboles].

Personalización de enlace externo

Colocar las reglas de enlace en un archivo externo aparte es útil cuando usted no tiene control directo sobre el archivo XSD, cuando está administrando documentos de esquemas grandes, o simplemente cuando desea separar los dos archivos para lograr una mayor claridad. Los archivos de personalización de enlace también son documentos de esquema XML con la extensión típica .xjb. La sintaxis general de un archivo de enlace se muestra en el Listado 3.

Listado 3. Síntaxis de archivo de enlace
<jaxb:bindings schemaLocation =
                "xsd:anyURI"> <jaxb:bindings schemaLocation=”file.xsd”
                node="xsd:string"> <binding declaration>
                <jaxb:bindings> </jaxb:bindings>

En donde:

  • schemaLocation se refiere al esquema remoto.
  • node es una expresión de XPath 1.0 que identifica el nodo de esquema al cual se asocia una determinada declaración de enlace (vea XML Path Language (Lenguaje XML Path) para obtener una descripción detallada de XPath).
  • <binding declaration>controla la forma en que se producen los artefactos de enlace de datos JAXB.

Las declaraciones de enlace se asocian a un enfoque, como se muestra en la Figura 8.

Figura 8. Alcance del enlace
Alcance del enlace

Consulte Customization syntax (Sintaxis de personalización) para obtener información más detallada. Supongamos que usted desea cambiar el nombre de una clase generada, de “Case” (Caso) a “Special Case” (Caso especial), como se muestra en el Listado 4.

Listado 4. Ejemplo de personalización
<jaxb:bindings
                xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema" jaxb:version="2.0">
                <jaxb:bindings schemaLocation="Businessitems.xsd"
                    node="/xsd:schema"><jaxb:bindings
                    node="xsd:complexType[@name='Case']"><jaxb:class
                    name="SpecialCase"
                /></jaxb:bindings></jaxb:bindings></jaxb:bindings>

Ahora es sencillo librarse de los tipos Java más exóticos usando el archivo de enlacetypes.xjb, como se muestra en el Listado 5.

Listado 5. Personalización de tipos
<jaxb:bindings
                xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema" jaxb:version="2.0">
                <jaxb:bindings schemaLocation="Businessitems.xsd" node="/xsd:schema">
                    <jaxb:globalBindings><jaxb:javaType
                    name="java.lang.Float" xmlType="xsd:decimal"
                    /><jaxb:javaType name="java.lang.Integer"
                    xmlType="xsd:integer" /><jaxb:javaType
                    name="java.util.Date" xmlType="xsd:date"
                /></jaxb:globalBindings></jaxb:bindings></jaxb:bindings>

Así mismo, usted puede convertir las enumeraciones XSD en tipos seguros Javaenum, como se muestra en el Listado 6.

Listado 6. Personalización de enumeración
<jaxb:bindings
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" jaxb:version="2.0">
<jaxb:bindings schemaLocation="Businessitems.xsd">
<jaxb:bindings
node="//xsd:element[@name='Status']/xsd:simpleType"><jaxb:typesafeEnumClass
name="Status"/></jaxb:bindings><jaxb:bindings
node="//xsd:element[@name='Product']/xsd:simpleType"><jaxb:typesafeEnumClass
name="Product"/></jaxb:bindings></jaxb:bindings></jaxb:bindings>

Las clases Java se regeneran con los archivos de enlaceenums.xjbytypes.xjb, como se muestra en la Figura 9.

Figura 9. Asistente XSD a Java con archivos de enlace
Asistente XSD a Java con archivos de enlace

Esto crea la clase Java con los tipos personalizados y una clase Java de tipo seguroenum, como se muestra en el Listado 7.

Listado 7. Clase Java personalizada
publicclassCase{protectedString
number;protectedFloat amount;protectedDate
created;protectedInteger sequence;protectedCase.Status
status;protectedCase.Product
product;protectedbooleanapproved;…
                publicenumStatus{OPEN
                ("Open"),CLOSED("Closed"),FORWARDED
                ("Forwarded");privatefinalString
value; … }

Anotaciones

Las clases generadas se enriquecen con anotaciones especiales JAXB que brindan el marco de tiempo de ejecución con los mapeos necesarios para procesar los correspondientes documentos XML. También se requieren las anotaciones para el mapeo inverso y para anular las reglas de enlace predeterminadas para el mapeo Java-to-XML Schema (Java a esquema XML). Todas las anotaciones del paquete javax.xml.bind.annotation se encuentran disponibles.

Una anotación clave es @XmlRootElement, la cual mapea una clase de alto nivel a un elemento global en el esquema XML que es usado por el WSDL del servicio web. A veces, sin embargo, el compilador JAXB se excede en sus atribuciones para adivinar qué clase merece esta anotación. Si ocurre esto, se visualizará el siguiente error de tiempo de ejecución:

Unable to marshal type "gov.mof.tax.businessitems.Case"
                as an element because it is missing an @XmlRootElement annotation.

Por lo tanto la pregunta es: ¿por qué JAXB pone @XmlRootElement a veces, pero no siempre? Este comportamiento intuitivo es interesante, pero no deja de ser complicado aclararlo. Esto puede ser poco práctico porque es muy probable que se generen clases Java frecuentemente y se requiere la edición manual para corregir esto. Nuevamente, un archivo de enlace externo viene al rescate, como se comentó en How to make XJC generate XmlRootElement with an external binding file [Cómo hacer que XJC genere XmlRootElement con un archivo de enlace externo].

Comportamiento polimórfico

Nuestra XSD potencia una característica interesante del esquema XML, la herencia. El agudo artículo Polymorphic Web services [servicios web polimórficos] describe detalladamente esta característica y explica cómo las extensiones XML y las técnicas de invocación de servicio dinámicas conducen al polimorfismo.

En el contexto de JAXB, consideremos el esquema que se muestra en la Figura 10.

Figura 10. Herencia en un esquema XML
Herencia en un esquema XML

BaseNotification deriva de TaxNotification y PaymentNotification, respectivamente. Éstos heredan todos los atributos y los elementos de la base, y agregan atributos propios nuevos, como se muestra en el Listado 8.

Listado 8. Clase Java personalizada
<xsd:complexType
abstract="true"name="BaseNotification">…
                </xsd:complexType><xsd:complexType
name="TaxNotification"><xsd:complexContent><xsd:extension
base="bo:BaseNotification"></xsd:extension>
                </xsd:complexContent>
                </xsd:complexType><xsd:complexType
name="PaymentNotification"><xsd:complexContent><xsd:extension
base="bo:BaseNotification">...

El tiempo de ejecución JAXB registra todos los tipos de clase Java que se necesitan a través de introspección. Ahora bien, cuando tratamos de pasar una subclase derivada, en términos de un tipo de base en tiempo de ejecución, las subclases no serán reconocidas simplemente por la introspección de los tipos de parámetros de interfaz.

Para explotar la herencia y también el polimorfismo, se introdujo una nueva anotación en JAXB 2.1 y en Java 6.0, javax.xml.bind.annotation.XmlSeeAlso. @XmlSeeAlso aborda el polimorfismo referenciándose a clases adicionales de subclases derivadas que se agregan a la clase JAXBContext, junto con aquellas clases derivadas de los parámetros de interfaz, como se muestra en el Listado 9.

Listing 9. Clase Java con herencia
@XmlSeeAlso({PaymentNotification.class,
    TaxNotification.class})publicabstractclassBaseNotification
{ ...

Tiempo de ejecución

Después de analizar la configuración básica, ahora veamos cómo podemos hacer funcionar JAXB. Necesitamos un proceso de negocios básico para demostrar cómo usted puede trabajar con tareas humanas, como se muestra en la Figura 11.

Figura 11. Proceso de negocios simple
Proceso de negocios simple

Usted importa este proceso de negocios a WebSphere Integration Developer y lo implanta en WebSphere Process Server. Puede descargar "TaxClient" del archivo sample_WID_project.zip suministrado. TaxClient es un proyecto Web que incluye una interfaz de usuario sencilla, como se muestra en la Figura 12.

Figura 12. Interfaz de usuario básica
Interfaz de usuario básica

Configuración de puntos finales

Primero necesita configurar los puntos finales de servicios web usados por el proxy de servicios web Java. Por ejemplo, un JavaBean usado en una página JSP puede representar las configuraciones proxy y es compartido por todo el proyecto, como se muestra en el Listado 10.

Listing 10. Configuración de los puntos finales proxy de servicios web
<%String bfmEndpoint =
ServicesBean.getInstance().getBfmEndpoint(); String htmEndpoint =
ServicesBean.getInstance().getHtmEndpoint(); if( request.getParameter(
"newBFMEndpoint" ) != null ) { bfmEndpoint = request.getParameter( "newBFMEndpoint"
); ServicesBean.getInstance().setBfmEndpoint( bfmEndpoint ); } if(
request.getParameter( "newHTMEndpoint" ) != null ) { htmEndpoint =
request.getParameter( "newHTMEndpoint" ); ServicesBean.getInstance().setHtmEndpoint(
htmEndpoint ); } %>

Luego se crean los proxies de tiempo de ejecución y se conservan en un Java Bean. Después de crear unas pocas instancias de proceso en Business Process Choreographer Explorer, ahora usted puede recibir y trabajar con los datos referidos a tareas humanas.

Recepción de datos de origen

La clase JAXBContext administra las relaciones de enlace requeridas por todas las operaciones posteriores, y es el punto de entrada para su aplicación Java. Usted obtiene una instancia nueva de esta clase mediante el uso del código que se muestra en el Listado 11.

Listado 11. Creación del contexto JAXB
JAXBContext jaxbContext =
                JAXBContext.newInstance( "gov.mof.tax.businessitems" );

Esta llamada inicializa el objeto JAXBContext para que administre las invocaciones de tiempo de ejecución. El argumento transmitido contiene una lista de nombres de paquetes Java separados por dos puntos (:) que incluyen clases derivadas de esquemas. La llamada JAXBContext.newInstance sin embargo, es costosa. Como por diseño es un subproceso seguro, usted podría almacenarlo en caché en una variable final estática y pública, o ajustarlo en un patrón singleton después de la creación para su posterior acceso.

Ya estamos listos para crear los otros artefactos principales de tiempo de ejecución, el Unmarshaller. Le permite convertir datos XML directamente a un árbol de objetos de contenido Java, como se muestra en la Figura 13.

Figura 13. El rol del unmarshaller
El rol del unmarshaller

Cualquier elemento XML global se puede desestructurar (unmarshall) si es anotado por @XmlSeeAlso como la raíz de un documento de instancia. El fragmento de código en el Listado 12 muestra cómo se crea el unmarshaller. Los elementos globales también se pueden fusionar a través de un conjunto de esquemas si están listados en contextPath.

Listado 12. Creación del unmarshaller
Unmarshaller unmarshaller =
                jaxbContext.createUnmarshaller();

El unmarshaller puede procesar datos XML provenientes de una amplia variedad de fuentes de datos, incluyendo archivos, flujos de entrada, localizadores URL, objetos DOM, analizadores SAX, y otros. En nuestro ejemplo, la fuente de datos se devuelve a través de la llamada claim() contra el proxy Human Task Manager - HTM (Administrador de tareas humanas - HTM) de WebSphere Process Server, como se muestra en el Listado 13.

Listado 13. Unmarshalling (desorganización) de la fuente de datos
Claim claim = new
Claim( ); claim.setTkiid( tkiid ); // returned via FacesContext Request Parameter
ClaimResponse response = htmProxy.claim( claim ); SOAPElement element =
response.get_any(); // Traverse to SDO node Iterator<SOAPElement> iter
= element.getChildElements(); if( iter.hasNext()) { SOAPElement operation =
iter.next(); Class<?> inputClass = … // via introspection from JAX-WS
WSDL unmarshaller.setValidating( true );JAXBElement<?>jaxbObject =
unmarshaller.unmarshal( operation, inputClass );inputObject =
jaxbObject.getValue(); if( inputObject instanceof inputClass ) { ... }
Iterator<SOAPElement> iterDetails = operation.getChildElements( );
while( iterDetails.hasNext()) { SOAPElement detail = iterDetails.next();
System.out.println( "Input " + detail.getLocalName()); ... } // while }
// if

La interfaz API de HTM para WebSphere Process Server devuelve los datos de entrada actuales para una tarea humana mediante la llamada claim(). La entrada de esta llamada es tkiid(ID de instancia de tarea) que se refiere a la instancia específica de una tarea humana, y devuelve los datos de entrada. En nuestro ejemplo, es devuelto por el cliente cuando el usuario selecciona una entrada de la lista de tareas, en donde tkiid se almacena como campo oculto.

Como el SOAPElement devuelto tiene una jerarquía de objetos por debajo, usted debe iterar a través del árbol para obtener el objeto de datos real. El inputClass esperado se resuelve mediante introspección de los artefactos JAX-WS WSDL. Por precaución, verifique el tipo de instancia del objeto devuelto.

El fragmento de código muestra la delicadeza y el poder de JAXB: una vez que todos los artefactos están preparados, recuperar y desorganizar (unmarshall) cualquier esquema XML/tipo Java es fácil, sin necesidad de comprender los mecanismos subyacentes del mapeo de esquema.

Validación de los datos de origen

Una característica útil de JAXB es la capacidad de validar los datos de origen contra el esquema asociados como parte de la operación de desorganización (unmarshalling).

Listing 14. Validación de la fuente de datos
unmarshaller.setValidating( true
                );

La línea en el Listado 14 le pide al tiempo de ejecución JAXB que informe si los datos no coinciden con el esquema. La especificación es difusa aquí: ordena a una implementación que informe los errores de validación, pero esto no se respeta estrictamente para detener el procesamiento de datos. Esto significa que la forma en que la implementación JAXB administra los documentos XML inválidos no está estrictamente definida.

La base común es que las implementaciones deben estar capacitadas para desorganizar (unmarshal) documentos válidos. Observe que si la validación está activada, las facets (facetas) del esquema XML, que restringen los tipos de datos básicos, se verifican laboriosamente.

Creación

Una vez que usted tiene el objeto raíz de un árbol arbitrariamente complejo, puede avanzar y trabajar con él. Sin embargo, como se señaló anteriormente, este árbol tal vez no esté totalmente construido si es la primera llamada, o si los valores no se configuraron anteriormente.

Un enfoque para “preconstruir” los objetos Java en memoria propone atravesar el árbol mediante reflejo, y resolver de manera recursiva los tipos complejos contenidos. Opcionalmente, usted puede configurar valores predeterminados si no son suministrados por la validación de esquema, como se muestra en el Listado 15.

Listado 15. Construcción del objeto Java
public void buildDataObject( Object
dataObject) { … Method[] methods = dataObject.getClass( ).getMethods(); for( Method
method : methods ) { String methodName = method.getName(); // Setters start with
"set" and have one param if( methodName.startsWith( "set" )) { Class[] parameters =
method.getParameterTypes(); // Check if the value is already set through the getter,
(boolean ‘is') byte[] byteArray = methodName.getBytes(); String getterName = new
String( byteArray ); if( parameters[0] == java.lang.Boolean.class || parameters[0]
== java.lang.Boolean.TYPE ) { getterName = methodName.replaceFirst( "set", "is" ); }
else // turn 'setter' into 'getter' { byteArray[0] = 'g'; getterName = new String(
byteArray ); } Method getter = dataObject.getClass().getMethod( getterName, new
Class[]{} ); Object result = getter.invoke( dataObject, new Object[]{} ); if(
parameters.length == 1 ) { Class parameter = parameters[0]; if( result != null ) {
// If the value is already set, skip if( parameter == java.lang.Integer.class || …
parameter == java.lang.Enum.class ) { continue; } // If this is a complex type,
recurse buildDataObject( result ); continue; } ...

Este método conveniente parte de las raíces del árbol hasta las hojas y lo construye y completa. Este es un patrón bastante común. Alternativamente, usted podría estar en condiciones de obtener las partes preconstruidas del árbol que son suministradas por otros sistemas. Puede agregarlas fácilmente usando el método que se muestra en el Listado 15.

Visualización dinámica del esquema

Hemos comentado la necesidad de adaptarse a definiciones XSD que cambian frecuentemente, y observamos cómo JAXB puede ayudar a hacer esto. Además, es útil tener una interfaz de usuario dinámica que básicamente haga lo mismo: recorriendo los objetos Java a través de reflejo y creando dinámicamente código HTML que represente los correspondientes campos de entradas.

Luego usted puede recopilar la entrada de usuario y devolverla a WebSphere Process Server. Analicemos primero el siguiente fragmento de código del Listado 16 que muestra cómo se puede construir un elemento que aporte tanta dinámica.

Para saber qué tipos entran y salen del sistema, referirse al lenguaje WSDL. Una forma de hacer esto programáticamente es creando un cliente Java y luego internalizando las clases Java creadas. Para hacer esto, recorra los WSDL y cree los clientes, como se muestra en la Figura 14.

Figura 14. Creación de los clientes WSDL
Creación de los clientes WSDL

El siguiente fragmento de código en el Listado 16 muestra cómo se leen estos metadatos.

Listado 16. Lectura de metadatos WSDL
…// Retrieve Helper for input // The standard
'getTypeDesc()' method provides the WSDL metadata Class<?> helper =
Class.forName( wsdlPackage + "." + operationName + "_Helper"); Method method =
helper.getMethod( "getTypeDesc", new Class[]{} ); TypeDesc typeDesc =
(TypeDesc)method.invoke( null, new Object[]{} ); FieldDesc[] fieldDesc
=typeDesc.getFields();for( FieldDesc field : fieldDesc ) { … inputName =
field.getXmlName().getLocalPart(); inputClass = Class.forName( inputPackage + "." +
field.getXmlType().getLocalPart()); … } // Retrieve Helper for output (Response)
...

Vea el paquete gov.mof.tax.bean del proyecto TaxClient suministrado en el archivo sample_WID_project.zip.

Listado 17. Creación del formulario (1)
 protected void createFormFromDataObject(
Object dataObject, boolean disabled ) { // Custom logic to check if data objects
have equal types, etc … delegate.beginRendering( ); createForm( dataObject, null,
"", htmlString, disabled); }

El método principalcreateFormFromDataObjecten el Listado 17 se llama con un objeto de datos, el cual representa la entrada y salida de tareas humanas. En un escenario real, una tarea humana más sofisticada podría usar diferentes tipos. Para proveer todos los tipos de tareas humanas, usted necesita soportar una cantidad ilimitada de objetos para entrada y salida.

Además, este método podría tener cierta lógica personalizada para detectar si los datos del objeto de entrada se pueden usar para llenar previamente el objeto de salida. Según cuánto quiera invertir, se puede involucrar bastante a este método. El método luego llama a createForm que realiza el trabajo real atravesando el árbol mediante reflejo. Si se encuentra un subárbol, el método se llama a sí mismo de manera recursiva, como se muestra en el Listado 18.

Listado 18. Creación del formulario (2)
private void createForm( Object dataObject,
String title, String methodName, StringBuffer htmlString, boolean disabled ) { …
Method[] methods = dataObject.getClass( ).getMethods(); for( Method method : methods
) { methodName = method.getName(); Class[] parameters = method.getParameterTypes();
String label; // Getters start with "get" or "is" (booleans) and have no param if(
parameters.length == 0 && (methodName.startsWith( "get") ||
methodName.startsWith( "is" ))) { … // Need setter when the values are retrieved
from the form byte[] byteArray = methodName.getBytes(); byteArray[0] = 's';
methodName = new String( byteArray ); Object result = method.invoke( dataObject, new
Object[0] ); if( result instanceof java.lang.Integer || … result instanceof
java.lang.Enum ) { delegate.addField( label, result, currentSDOName, methodName,
disabled ); } else // if nested, recurse {createForm( result, label,
                methodName, htmlString, disabled ); } } // if } // for

La tarea humana puede adoptar varios estados. Si no es “invocado” (“claimed”), usted podría presentar a los campos como desactivados. Una vez que es invocado por el usuario, se activan los campos y el usuario puede comenzar a ingresar datos.

Para simplificar el proceso de creación del lenguaje de marcado, la clase Delegate tiene la tarea de diseñar el código HTML real. Esto significa que Delegate encapsulará la lógica para la generación de interfaz de usuario, en tanto que el “traductor” mencionado anteriormente se mantendrá relativamente estable. Delegate mantiene un Map de elementos y es invocado cuando se debe suministrar una clase Java nueva en el árbol o un campo, como se muestra en el Listado 19.

Listado 19. Delegar la creación de HMTL
 public class Delegate { private String
processTemplate; private Map<String, StringBuffer> fieldMap; …
protected void beginRendering( ) { fieldMap = new TreeMap( ); } protected void
addField( String fieldName, Object fieldValue, String sdoName, String methodName,
boolean disabled ) { StringBuffer htmlBuffer = new StringBuffer( "" ); if(
fieldValue instanceof java.lang.Integer || fieldValue instanceof java.lang.Float ||
fieldValue instanceof java.lang.String ) { htmlBuffer.append(
"<tr><td>" + fieldName +
":</td><td>” + "<input name='" + sdoName + "." +
methodName + "' size='20' value='" + fieldValue + "'" + (disabled? " disabled" : "")
+ "/></td></tr>\n" ); } else if( fieldValue
instanceof java.lang.Boolean ) { … } else if( fieldValue instanceof java.lang.Enum )
{ htmlBuffer.append( "<tr><td>" + fieldName + ":
</td><td><select name='" + sdoName + "." +
methodName + "' " + (disabled? " disabled" : "" ) + " >\n" ); Object[]
enumConstants = fieldValue.getClass().getEnumConstants(); for( Object enumConstant :
enumConstants ) { Object enumValue = null; Method valueMethod =
enumConstant.getClass().getMethod( "value", new Class[]{} ); enumValue =
valueMethod.invoke( enumConstant, new Object[]{} ); htmlBuffer.append(
"<option value='" + enumValue + "'>" + enumValue +
"</option>\n" ); } htmlBuffer.append(
"</select></td></tr>\n" ); } StringBuffer
sb; if(( sb = fieldMap.get( sdoName )) != null ) { sb.append( htmlBuffer ); } else {
fieldMap.put( sdoName, htmlBuffer ); } }

El código es sencillo y sólo crea salida HTML producto de los campos que están en el formulario. Sin embargo, es importante el dato fieldNames de los elementos del formulario. Esto se necesitará para obtener los datos de usuario cuando usted devuelva esa salida al Human Task Manager de WebSphere Process Server.

Luego se suministra el marcado final en una JSP o JSF, como se muestra en el Listado 20.

Listado 20. Suministrar el código HMTL en JSP
<jsp:useBean id="td"
                scope="session" class="gov.mof.tax.bean.ToDoTaskBean" />
                </head> <body bgcolor="#efefef"> <div
                class="formblock"> <form> <% td.createInputForm();
                %> <% out.print( td.getHtmlString() ); %>
                <h2>Possible Activities:</h2> <% if(
                td.getCompleteSuccessful() ) { %> <input id="close" type="submit"
                name="close" value="Close Window" /> <% } else { %>
                <input id="claim" type="submit" name="claim" value="Claim"
                <%=(td.getClaimButton() == true) ? "" : "disabled"%> />
                <input id="cancel" type="submit" name="cancel" value="Cancel"
                <%=(td.getClaimButton() == false) ? "" : "disabled"%> />
                <input id="complete" type="submit" name="complete" value="Complete"
                <%=(td.getClaimButton() == false) ? "" : "disabled"%> />
                <input id="tkiid" type="hidden" name="tkiid"
                value="<%=td.getTkiid()%>" /> <% } %>
                </form> </div> </body>

El resultado se muestra en la Figura 15.

Figura 15. Frontend dinámico
Frontend dinámico

Los tipos de campos siguen el esquema XML.Created es suministrado como campo de fecha, indicado por el ícono de la derecha. Con la ayuda del archivo de personalización que se mencionó anteriormente, las enumeraciones de esquema, como Status y Product, fueron mapeadas a Java enums, y el código puede brindarlas como menús desplegables. El atributo Approved fue mapeado a un Boolean Java y es suministrado como una casilla de verificación.

Si usted está usando una definición XSD bastante compleja, como en el ejemplo, todos los campos se mostrarán de una manera bastante desordenada. Puede resultar difícil y también confuso trabajar con un formulario así. Por este motivo, usamos la biblioteca JavaScript Armel Pingault's apTabs que le permite fragmentar el formulario en una serie de pestañas.

Para devolver los datos, usted tiene que capturar la entrada de usuario y llamar al método completo. Por este motivo le dimos a todos los campos nombres completamente aceptados que se parecen remotamente a las respectivas direcciones XPath, como se muestra en el Listado 21 (se agregó sangría para una mayor legibilidad).

Listing 21. Código HMTL que visualiza y captura los datos de entrada
...<table border=0 bgcolor='#f2f5ff' width='98%'
                align='center'> <tr>
                <td>FirstName:</td> <td>
                    <inputname='.setObjection.setPrepared.setFirstName'size='20'
                value='Fred'/> </td> </tr> <tr>
                <td>Lastname:</td> <td>
                    <inputname='.setObjection.setPrepared.setLastname'size='20'
                value='Flintstone'/> </td> </tr> ...

Como se comentó anteriormente, lo botones sobre la parte inferior del formulario llaman a los respectivos métodos de ciclo de vida de WebSphere Process Server. Los botones Claim y Cancel se activan o desactivan reflejando el estado actual de la tarea humana, y también activan o desactivan todos los elementos UI. Pasar de uno a otro de estos dos estados es una buena prueba para verificar la funcionalidad básica de cliente.

Ensamblado de la entrada de usuario

Ahora todo lo que nos queda por hacer es obtener los cambios del usuario y devolverlos a WebSphere Process Server. El método principal para completar una tarea humana pendiente es, usted lo adivinó, llamando a complete() o a una de sus variantes.

El botón Complete (Completo) anterior llama al método getDataFromForm, el cual sigue el mismo patrón: llamando al verdadero caballito de batalla,getData, que se desplaza a través del árbol y se llama a sí mismo de manera recursiva de ser necesario, como se muestra en el Listado 22.

Listado 22. Recuperación de la entrada de usuario
protected void getDataFromForm(
Object dataObject, Map<String, String[]> map) { // Traverse through
request parameter for( String key : map.keySet()) { String mapValue =
map.get(key)[0]; formsBean.getData( dataObject, key, mapValue ); } }
private void getData( Object dataObject, String key, String value ) { … // Parse the
key to get dataObject field String[] array = key.split( "\\."); for( int index = 0;
index < array.length; index++ ) { String element = array[index]; if(
element.startsWith( "set" ) || element.startsWith( "is" )) { Method[] methods =
dataObject.getClass().getMethods(); for( Method method : methods ) { Class[]
parameters = method.getParameterTypes(); if( parameters.length == 1
&& method.getName().equals( element )) { Class parameter =
parameters[0]; if( parameter == java.lang.Integer.class ) { method.invoke(
dataObject, Integer.valueOf( value )); } else if( parameter ==
java.lang.Integer.TYPE)…}else if( parameter.isEnum()) { // Create an
instance of an enum with 'fromValue' method Method enumMethod = parameter.getMethod(
"fromValue", String.class ); Object newEnum = enumMethod.invoke( parameter, value );
// Call setter to set the new Enum method.invoke( dataObject, newEnum ); } else //
object is already built, get the pointer and recurse { byte[] byteArray =
element.getBytes(); byteArray[0] = 'g'; String methodName = new String( byteArray );
Method getter = dataObject.getClass().getMethod( methodName, new Class[]{} ); Object
newDataObject = getter.invoke( dataObject, new Object[]{} ); if( parameter !=
Enum.class ) // Complex types: recurse { String newArray = ""; for( int i = 1; i
< array.length; i++ ) { newArray += array[i] + "."; }getData(
newDataObject, newArray, value ); } } break; } } // for } // if } // for }

Envío por la conexión

El árbol de datos ahora refleja la entrada de usuario y usted puede seguir avanzando y devolverlo, como se muestra en la Figura 16.

Figura 16. Marshalling (Organización) de datos de usuario
Marshalling (Organización) de datos de usuario

Nuevamente, JAXB facilita el proceso marshalling (de organización). Usted crea un objeto apropiado para ser transmitido a la interfaz API de servicios web de WebSphere Process Server, y luego crea una respuesta soapMessage. Esto luego es transmitido al marshaller. Los metadatos WSDL se recuperan a través de los artefactos de tiempo de ejecución JAX-WS creados y del reflejo.

Luego usted atraviesa el mapa de parámetros de solicitudes Faces, manteniendo los datos de usuario real y colocando cada uno de esos elementos ensoapMessage, como se muestra en el Listado 23.

Listado 23. Marshalling (organización) de datos de usuario
...CompleteWithOutput
completeWithOutput = new CompleteWithOutput(); SOAPFactory soapFactory =
SOAPFactory.newInstance(); SOAPElement soapMessage = soapFactory.createElement(
operationName + "Response", "nsprefix", wsdlNamespace ); SOAPElement topElement =
soapFactory.createElement( outputName ); soapMessage.addChildElement( topElement );
// Get http parameters from Faces Context Map<String, String[]> map =
FacesContext.getCurrentInstance().
getExternalContext().getRequestParameterValuesMap(); getDataFromForm(
inputDataObject, map ); marshaller.marshal( inputDataObject, topElement );
completeWithOutput.set_any( soapMessage ); completeWithOutput.setTkiid( tkiid );
CompleteWithOutputResponse response = htmProxy.completeWithOutput
(completeWithOutput);

Esto completa la tarea humana.


Conclusión

Este artículo introdujo conceptos JAXB básicos y cómo usarlos conjuntamente con la interfaz API de servicios web de WebSphere Process Server. JAXB salva delicadamente las distancias existentes entre los documentos XML enviados por la conexión y las clases Java con las que usted trabajará dentro de su entorno de cliente.

Esta información se focalizó en la manera en que el desarrollo JAXB y el entorno de tiempo de ejecución pueden simplificar el proceso de mapeo de XSD a Java, y también se concentró en la forma de usar las características del tiempo de ejecución para desarrollar una interfaz de usuario simple que se genere dinámicamente en tiempo de ejecución a través de tiempo de ejecución JAXB y reflejo Java.


Descargar

DescripciónNombretamaño
Code samplesample_WID_project.zip1,141KB

Recursos

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=WebSphere
ArticleID=493981
ArticleTitle=Interacción de WebSphere Process Server mediante interfaz API de servicios web y método de organización JAXB
publish-date=08032011