Conversión de servicios SOAP a REST/JSON usando OSB 11g

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:



package com.oracle.osb.samples.rest.json.demo;

import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import net.sf.json.xml.XMLSerializer;

/**
 * Clase encargada de realizar la conversión de JSON a XML 
 * y viceversa
 *
 * @author Sandra
 */
public class JSONXMLConverter {

      public JSONXMLConverter() {
      }
     
      /**
       * 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);
            return json.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 = new XMLSerializer();
            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.

Comentarios

  1. Perfecto, muy bien explicado Ingeniera =)

    ResponderEliminar
  2. Hola Sandra.
    tengo un JAR que convierte un formato en particular y lo devuleve en un objeto(POJO). Ahora lo que no puedo es como accedo al contenido de ese objeto en el proxy para poder devolverlo como XML.

    Saludos y gracias.

    ResponderEliminar
    Respuestas
    1. Hola Freddy

      En realidad el jar que estoy usando es para convertir de XML a JSON y viseversa, y ambos métodos reciben y regresan un String simple. Por eso es que lo puedo usar con un java callout en el pipeline del proxy. Luego ese mismo string lo parseo a XML con la función inlinedXML para poder manipularlo en el flujo. Me parece que lo que tu quieres hacer no es viable, ya que son tipos de datos no soportados en OSB. Se me ocurre que para lograrlo tuvieras que definir una estructura similar y declarar una variable de este tipo, pero no estoy muy segura que se pueda.

      Saludos!

      Eliminar
    2. Gracias Sandra eres la mejor. Te cuento un poco como lo solucioné y me comentas si algo puede salir mal.

      He creado una clase en Java que utilice el JAR que te comenté, para que me devuelva como un XML en string(De Objeto a XML), por tanto estos 2 JAR lo utilizo dentro de OSB. Ahora como ya la respuesta es en string utilice lo que me mencionas inlinedXML y se acabo el problema.

      Gracias mil gracias.

      Eliminar
    3. Que bueno que lo has logrado Freddy, me da gusto saberlo.

      No veo algún problema si ya lo puedes usar como xml en OSB.

      Saludos!

      Eliminar
  3. hola sandra.


    tengo una pregunta.


    Como puedo encolar un mensaje desde el OSB utilizando ActiveMQ?


    Saludos

    ResponderEliminar
    Respuestas
    1. Hola Carlos

      Lo que tienes que hacer es primero agregar los jars de ActiveMQ al path de tu weblogic, esto es, subes los jars a la ubicación de tu dominio OSB, y luego en el archivo setDomainEnv agregas a la variable PRE_CLASSPATH el jar... por ejemplo: set PRE_CLASSPATH=%DOMAIN_HOME%\lib\activemq-all-5.9.1.jar; Reinicias el weblogic y todo el dominio.

      Luego, en la consola de administración de weblogic tienes que crear el servidor JMS, aquí especificas el connection factory y la cola a la que te quieres conectar.

      En el business service de OSB, elijes el transporte JMS y el jndi que creaste, tal como si fuera local, por ejemplo jms://localhost:7011//

      Después, en algún pipeline de un Proxy Service puedes hacer un service callout al Business Service que creaste con un mensaje xml determinado como request.

      Espero haber entendido tu pregunta y que sea clara la explicación.

      Saludos!

      Eliminar
  4. hola tengo un problema con SOA 11g
    genere un business rule y lo expuse como webservice stanalone,
    despues genere un SOA composite y con un bpel llamo al BR vis webservices y lo alimento, me genera su out varieble bien, pero al querer leerlo con una asignacion a otra variable me dice que esta vacia, no encuentro cual es el problema

    ResponderEliminar
  5. Hola Sandra, primero que todo quiero agradecerte por el tutorial, es muy concreto y fácil de entender, por otro lado, quiero comentarte un problema que se me esta presentando con el programa JSONXMLConverter, sucede que realice la respectiva implementación en NetBeans, agregué las dependencias e hice las pruebas, hasta el momento todo perfecto, pero cuando realice la carga en el OSB, me generó inconsistencias solicitando más dependencias, las cuales incorporé pero cada vez me necesita mas y mas. Me gustaría saber si tu sabes por qué se presenta esto? Y cómo solucionarlo o evitar que suceda?

    De antemano muchísimas gracias.

    ResponderEliminar
    Respuestas
    1. Hola Eduardo, te agradezco mucho los comentarios.

      Respecto a tu pregunta, me temo que en la versión 11g del OSB, como no tiene esta funcionalidad nativa, es necesario agregar cada uno de los jars de los cuales depende, me imagino que en Netbeans ya tendrías algunos de ellos. En la versión 12c ya no es necesario todo esto.

      Ya tuviste éxito en tu conversión en OSB?

      Saludos!

      Eliminar
    2. Hola Sandra, si ya funcionó, aparentemente no me tomaba bien las dependencias entonces las coloque carpeta lib del dominio y al parecer, existía un conflicto para encontrar las referencias. Muchas gracias por estar pendiente y en especial por este maravilloso blog.

      Eliminar
    3. Excelente Eduardo, me da gusto que lo hayas podido resolver!

      Recibe un saludo.

      Eliminar
  6. Hola buen día!!

    Estoy trabajando con OSB y al pedir una petición de los servicios expuestos en el bus a través de ajax no muestra la respuesta de la solicitud, al depurar el código en el navegador me aparece este error "XMLHttpRequest cannot load http://localhost:7001/OSBCapled/Proxy_Services/AutenticacionWS?WSDL. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:11715' is therefore not allowed access." El error aparece porque falta configurar en el servidor el Cross Domain, mi pregunta es ¿Sabes como hacer eso en Weblogic? ¿Me podrías ayudar con este tema? Por favor gracias!!!

    ResponderEliminar

Publicar un comentario

Entradas más populares de este blog

OWSM and WS-Security: Username Token Authentication for SOAP and REST Services in OSB 12c.

¿Qué es un Enterprise Service Bus y por qué usarlo?