Hola a todos, esta vez la entrada está enfocada al uso de la herramienta Oracle Service Bus 11g para realizar la transformación de mensajes en formato XML hacia servicios SOAP, a formato JSON y exponerlo como servicios REST.

La creciente tendencia de las aplicaciones móviles o de las aplicaciones ligeras, es usar Servicios Web de tipo REST. Si consideramos, a manera de ejemplo, que nuestro Cliente que ya expone sus Servicios Web SOAP, desea consumirlos también en una aplicación móvil usando REST, existe la necesidad de realizar este paso de alguna manera, como desarrolladores y arquitectos tenemos la responsabilidad de poner en práctica uno de los principios básicos de SOA, la reutilización. La idea es usar los servicios existentes sin duplicarlos, simplemente explotar la funcionalidad que provee el OSB para realizar dicha transformación.

Este tema viene a flote, debido a que hace unos días, el viernes 26 de Julio de 2013 para ser exactos, tuvo lugar el primer evento del OTN Tour en México y tuve la oportunidad de participar y exponer este tema en una conferencia apoyando a mi jefe, Rolando Carrasco, uno de los dos Oracle Ace de México y organizador del evento (para quien no conoce este programa de Oracle y quiere enterarse de que trata, puede ver la página  http://www.oracle.com/technetwork/community/oracle-ace/index.html) Durante la plática dimos una pequeña demostración de como resolver este tema. A continuación detallaré la solución.

Las herramientas y versiones que usé para esta demo son:

  • OSB 11.1.1.6
  • JDK 1.6
  • Web Service de ejemplo http://www.predic8.com/material/ArticleService?wsdl
  • Proyecto java JSONXMLConverter, disponible en la página de ejemplos de Oracle. De cualquier manera pondré el código java más abajo por si no lo encuentran.
  • Los jars que usé para el converter (seguramente pueden prescindir de algunos) son:
  • commons-beanutils-1.8.3.jar
  • commons-beanutils-bean-collections-1.8.3.jar
  • commons-beanutils-core-1.8.3.jar
  • commons-collections-3.2.1.jar
  • commons-lang-2.6.jar
  • commons-logging-1.1.1.jar
  • commons-logging-adapters-1.1.1.jar
  • commons-logging-api-1.1.1.jar
  • ezmorph-1.0.6.jar
  • json-lib-2.4-jdk15.jar
  • junit-4.8.2.jar
  • xom-1.2.6.jar

La solución consta de la siguiente estructura:

El Servicio Web ArticleService de tipo SOAP expone 4 métodos: get, getAll, create y delete. En el OSB se exponen 4 Servicios Web Proxy de tipo REST; GetArticleProxyREST, GetAllArticleProxyREST, CreateArticleProxyREST y DeleteArticleProxyREST, cada uno relacionado a la operación correspondiente.

El servicio ArticleProxyREST también alojado en el OSB, expone la funcionalidad de los 4 proxys anteriores (a manera de Proxy de Proxys) a través de una sola url, que además de los datos del payload, recibe un atributo en el JSON denominado metodo, el cual cumple la función de determinar hacia cual de los 4 proxys redireccionará. Si no se desea usar este proxy, se pueden usar directamente los 4 proxys con su respectiva url en vez de solo una (la del proxy más externo).

1. El primer paso a realizar es la creación del proyecto java JSONXMLConverter, yo usé el de ejemplo de Oracle como base, pero tuve que agregar funcionalidad adicional debido a que la conversión no siempre es tan sencilla como desearíamos, por los prefijos y namespaces de cada servicio soap.

El código java queda de esta manera:


packagecom.oracle.osb.samples.rest.json.demo;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
importnet.sf.json.xml.XMLSerializer;
/**
 * Clase encargada de realizar la conversión de JSON a XML 
 * y viceversa
 *
 * @author Sandra
 */
public class JSONXMLConverter {
      publicJSONXMLConverter() {
      }
     
      /**
       * Método que realiza la conversión de una cadena en formato 
       * XML a una en formato JSON
       *
       * @param strXML  String con el xml bien formado y validado
       * @return        String con el JSON generado
       */
      public static String xml2json(String strXML) {
            XMLSerializer xmlSer = new XMLSerializer();
            xmlSer.setRootName(«JSON»);
            xmlSer.setTypeHintsEnabled(false);
            xmlSer.setSkipNamespaces(true);
            JSONObject json = (JSONObject) xmlSer.read(strXML);
            returnjson.toString();
      }
      /**
       * Método que realiza la conversión de una cadena en formato 
       * JSON a una en formato XML calificado
       *
       * @param strJSON       String con el JSON bien formado 
       *                      y validado
       * @param prefixRootN   Prefijo que se usará para el 
       *                      namespace del xml
       * @param rootName      Nombre del elemento raíz que tendrá
       *                      el xml
       * @param uri           Uri del namespace con el que se 
       *                      calificará el xml
       * @return              String con el xml generado
       */
      public static String json2xmlGenerico(String strJSON, 
         String prefixRootN, String rootName, String uri) {
           
            net.sf.json.JSON json = JSONSerializer.toJSON(strJSON);
            XMLSerializer xmlSer = newXMLSerializer();
            xmlSer.setRootName(prefixRootN + «:» + rootName);
            xmlSer.addNamespace(prefixRootN, uri);
            String xml = xmlSer.write(json);
            return xml;
      }
}

Yo les aconsejo probar estos métodos (o los que le agreguen), antes de generar el jar. Ya que es más fácil hacer el debug y encontrar fallos en un IDE adecuado para esto, cuando esté ejecutándose en el OSB los errores no van a ser tan descriptivos o fáciles de identificar. Una vez probada la funcionalidad de los métodos, el siguiente paso es generar el jar de la aplicación, mismo que usaremos más adelante.

2. Lo siguiente es, en la consola de desarrollo del Service Bus (o en Eclipse para los que tengan instalado el plugin) crear el proyecto. Comenzaremos con la estructura de carpetas, no existe un estándar, sin embargo lo recomendado por los expertos es algo como esta:

3. En la carpeta WSDL importaremos el correspondiente archivo del servicio ArticleService como un Recurso desde el URL.

4. Generamos el Business Service Articles_BS a partir del WSDL:

5. El siguiente paso es crear en la carpeta ProxyServices, el servicio proxy ArticleProxySOAP que estará basado en el business service anterior. Este paso es solo para ejemplificar que el mismo servicio puede estar expuesto como SOAP y como REST en el OSB. Sin embargo no es necesario realizarlo.

6. Ahora, en la carpeta Articles crearemos los proxys GetArticleProxyREST, GetAllArticleProxyREST, CreateArticleProxyREST y DeleteArticleProxyREST. Para este ejemplo solo me enfocaré en el primero, el resto los pueden realizar de la misma manera. Es importante prestar atención en este paso, ya que el proxy REST no es igual que los que se crean para SOAP. Debe ser de tipo Messaging Service y el tipo de Message Request y Response debe ser Text. El resto de las opciones pueden ser las de default.

7. En la carpeta JARS subiremos el jar que generamos en el paso 1, como recurso de utilería.

8. En el proxy GetArticleProxyREST, editar el flujo de mensajes:



9. Crear la siguiente estructura, que incluye un Pipeline Pair y dos Stages, uno de request y otro de response:


 10. Editar el stage de request y agregar una actividad Assign:
 $body/text() a la variable strBodyJSON


11. Agregar una actividad Java Callout e invocar el método json2xmlGenerico con los parámetros: «ns», «get», «http://predic8.com/wsdl/material/ArticleService/1/» y dejar el resultado en la variable strBodyXML.

12. Agregar otro Assign con la instrucción fn-bea:inlinedXML($strBodyXML) en la variable bodyXML. Esta función realizará la conversión del string con texto XML a un objeto XML propiamente, para poder ser manipulado en el OSB.



13. Agregar un Replace en la variable bodyXML para eliminar el atributo metodo del payload y dejar un texto en blanco.



14. Agregar una actividad Service Callout para invocar el método get del Business Service Articles_BS usando las variables bodyXML como Request y bodyXMLOut como Response en el Payload Document


15. Editar el stage de response y agregar un Assign con la función fn-bea:serialize($bodyXMLOut) en la variable strMsgXMLRespuesta.

16. A continuación agregar una actividad Java Callout e invocamos el método xml2json con el parámetro: $strMsgXMLRespuesta y dejar el resultado en la variable respuestaJSON.


17. Agregar un Replace de todo el contenido del nodo en la variable body por $respuestaJSON


Los siguientes pasos son para crear el Proxy más externo, ArticleProxyREST.

18. En la carpeta Proxy Services creamos el servicio ArticleProxyREST, de la misma manera que lo hicimos en el paso 6.

19. Editamos el flujo del servicio y creamos la siguiente estructura que consta de un Pipeline Pair, un Stage de request y un Conditional Branch.


20. Editamos el Branch y agregamos los registros: Operador: =, Valor: «create», «get», «getAll», «delete» y Etiqueta: create, get, getAll, delete.

21. Editamos el stage de request y agregamos un Assign con $body/text() en la variable strBodyJSON.

22. Agregamos una actividad Java Callout e invocamos el método json2xml con el parámetro: $strBodyJSON y dejamos el resultado en la variable strBodyXML.

23. A continuación agregar un Assign con la función fn-bea:inlinedXML($strBodyXML) en la variable bodyXML.

24. Como último paso agregamos un Assign con la función $bodyXML/metodo/text() en la variable metodo. Guardamos todo y activamos los cambios.

En este punto estamos listos para realizar una prueba de lo creado. Para esto podemos usar la consola del OSB o usar un cliente REST. Puede ser algún plugin para Chrome como por ejemplo Simple REST Client. En mi caso usaré este plugin. Les recomiendo que prueben ambos proxys, el SOAP y el REST y verifiquen las respuestas.

Probaré el servicio REST con el payload:
{
   «metodo» = «get»,
   «id»:»AR-00001″
}

¡Y voilà! Esta es la respuesta obtenida.

Para probar los demás métodos, pueden usar los siguientes JSON:

/************* CREATE ***************/
{
   «metodo» = «create»,
   «article»:
   {
         «name»:»MiArticulo»,
         «description»:»FTGFOP1, 1kg»,
         «price»:
          {
              «amount»:21.70,
              «currency»:»EUR»
          },
         «id»:»»
     }
}

/************* DELETE ****************/
{
   «metodo» = «delete»,
   «id»:»AR-00123″
}

/************* GET ALL ***************/
{
   «metodo» = «getAll»,
   «getAll»: «»
}

Como nota adicional, les platico el caso particular del método getAll. Este método no recibe parámetros, por lo que el flujo del proxy GetAllArticleProxyREST varía de los demás. El stage de request  queda de la siguiente manera:

Donde el Assign contiene la función:

fn-bea:inlinedXML(«
<soapenv:Body xmlns:soapenv=’http://schemas.xmlsoap.org/soap/envelope/’>
   <ns:getAll xmlns:ns=’http://predic8.com/wsdl/material/ArticleService/1/’ />
</soapenv:Body>
«)

Espero haya sido de su interés y les ayude en algo.

Hasta la próxima.

Sandy
Compartamos para trascender.