domingo, noviembre 15, 2015

Consumir con RPGLE Servicios Web REST creados en Genexus



En muchas de las ocasiones es necesario integrar aplicaciones creadas con Genexus y el generador RPG mediante la utilizaciòn de los servicios web Rest.

Porque utilizar servicios web REST, justamente poque resulta mas facil realizar el parseo de datos enviados y recibidos, con SOAP, se tiene mas complicaciones en el sentido de armar bien el XML.

Realmente la independencia que se tiene con REST resulta sencillo consumirlos de una forma rapida sin muchos dolores de cabeza, ya que el trabajar con RPGLE es bastante engorroso por su sintaxis y sobre todo su editor.


Tambien se tuvo problemas con el manejo de apuntadores, en algunos casos no fue posible generan ciertos errores al compilar y sobre todo al ejecutar, la información que supuestamente deberia disponer no estaba tan visible con este concepto.

De hecho que pudo revisar algunas librerias y ejemplos que ayudan a tener una vision muy clara del tema, pero lo mas gravitante del problema es conocer RPGLE, para ello recomiendo el siguiente libro: Guia del Programador RPGLE en español

Es importante resaltar que es necesario revisar las siguientes librerias, que permiten  conectar el ISERIES con servicios web externos,  un excelente trabajo de Robert Cozzi y algunos otros amantes del ISERIES:



Un sitio con abundante información sobre RPGLE, SOAP y JSON .
  1. Scott Klement
  2. Documentacion
  3. Manejo de Apuntadores con RPGLE

Como instalar en el ISERIES  - Instrucciones:  

Descargar la Libreria: Libreria ISOCKETS
 
En la PC desempaquetar el ZIP en una carpeta, luego realice lo siguiente:
FTP iseriestest usuario password
FTP QGPL/iSockets 
BINARY
QUOTE SITE NAMEFMT 1
CD /QSYS.LIB/QGPL.LIB
LCD "C:\[carpeta donde esta el archivo iSockets.savf]
QUOTE RCMD CRTSAVF QGPL/ISOCKETS
PUT iSOCKETS.SAVF
QUIT


En el ISERIES, restauramos la libreria con el siguiente comando:
RSTLIB SAVLIB(ISOCKETS) DEV(*SAVF) SAVF(QGPL/ISOCKETS)

Como utilizar ISOCKETS desde RPG IV?

En este link existen algunas instrucciones con ejemplos para utilizar de mejor manera la libreria  iSockets ===> , Ejemplos.

Para el Caso tambien hemos creado un servicio web REST EMISOR con un Data Provider de tal forma que pueda ser leido con el Metodo GET, si deseamos utilizar el metodo POST debemos utilizar un Procedimiento, en virtud de ello para mayor facilidad ya que solo se trata de recuperar informaciòn que es nuestro caso utilizaremos un DataProvider.

En nuestro caso hemos trabajado con JDK1.6 y Tomcat7.

En las Reglas del DataProvider:
// Parametros
Parm(In:&Numerocliente); 
Tener presente que el nombre del parametro es CASE SENSITIVE.


En el SOURCE:
 SDTConsultaClientes    
      Where ClienteCodigo = &Cliente
    {
        Cliente         = ClienteCodigo
        Nombres      = ClienteNombres
        Estado         = ClienteEstado
        codigoError   = '0000'

        mensajeError = 'PROCESO OK'
    }

En las propiedades:
Expose as Web Service = True
WebServices Protocol   =  REST Protocol

El ejemplo que a continuacion se detalla fue adoptado en base al codigo publicado en la siguiente URL: Ejemplo con RPGLE e ISOCKETS - GetUrlData

Se tuvo algunos problemas al utilizar la librerias YAJL, dicha libreria  que permiten leer y escribir JSON, en nuestro caso especifico se requeria solo leer un JSON, de tal forma que se opte por acoplar un algoritmo que permita cumplir con ese objetivo, aquello facilito el consumo del servicio web desde el ISERIES, con estos antecendentes se procedio a crear el mismo, tomando como ejemplo de la libreria ISOCKETS el siguiente programa RPGLE:


      H OPTION(*NODEBUGIO:*SRCSTMT)  BNDDIR('ISOCKETS':'QC2LE')
      /IF DEFINED(*CRTBNDRPG)
     H DFTACTGRP(*NO)
      /ELSE
     H NOMAIN
      /ENDIF

      /COPY iSockets/QCPYSRC,iSockets
      /COPY iSockets/QCPYSRC,cprotos

      /IF NOT DEFINED(*CRTBNDRPG)
     D GetURLData      PR            10I 0
     D  URL                         128A   Const
     D  rtnHTML                   65535A   OPTIONS(*VARSIZE)
     D  nRtnLen                      10I 0 Const


     P GetURLData      B                   Export
     D GetURLData      PI            10I 0
     D  URL                         128A   Const
     D  rtnHTML                   65535A   OPTIONS(*VARSIZE)
     D  nRtnLen                      10I 0 Const
      /ELSE
     D URL             S            128A
      /ENDIF


     D SALIDA          S           2000A
     D LARGO           S             10A
     D cRet            S            100A   dim(30)   
     D szURL           S            128A   Varying
     D hURL            S             10I 0
     D nBytes          S             10I 0
     D szHtml          S          65535A
     D pHtml           S               *   Inz(%addr(szHtml))
     D nLen            S             10I 0
     D nPage           S             10I 0
     D nPageB          S             10I 0
     D nPageF          S             10I 0
     D szDomain        S                   Like(szURL)
     D szPage          S                   Like(szURL)
     D szParms         S                   Like(szURL)


     D cJson           S           2000A
     D nPos            S             10I00
     D nPosIni         S             10I00
     D nPosFin         S             10I00
     D cReturn         S           2000A
     D nLarJson        S             10I00
     D nLargo          S             10I00
     D nUbiIni         S             10I00
     D nUbiFin         S             10I00
     D nLarCpo         S             10I00
     D cCampo          S            100A
     D cValor          S            100A
     D nError          S            010I00
     D nNro            S            010I00
     D cLinea          S            050A
     D cBuscar         S            020A
     D cCliente         S            010A

      /IF DEFINED(*CRTBNDRPG)
     C     *ENTRY        PLIST
     C                   PARM                    cCliente    // Parametro de Entrada Cliente
     C                   PARM                    cRet         // Retorna dato en unarreglo

      /free
           URL = 'http://localhost:8080/swrest/rest'
               + '/consultaccliente?Clientecodigo='
               + %trim(ccliente);
      /end-free

      /ENDIF

     C*                  callp      Joblog('URL-->%s':URL)   

     C* Permite desplegar en JOBLOG de la SESION el contenido de la Variable
     C                   callp     iSocketsOptions('DBG':*ON)

     C                   if        %Parms >= 1
     C                   eval      szUrl = %trimR(URL)
     C                   endif
     C                   if        szURL = ''
     C                   return
     C                   endif

      **  Find the end of the URL
     C                   eval      nLen = %scan(' ':szURL)
     C                   if        nLen > 0 and nLen < %len(szURL)
     C                   eval      %len(szURL) = nLen
     C                   endif

     C                   if        stricmp(%subst(szURL:1:5):'http:')=0 or
     C                              stricmp(%subst(szURL:1:6):'https:')=0
     C                   eval      nPageF = %scan('//':szURL)
     C                   if        nPageF > 0
     C                   eval      szUrl = %subst(szURL:nPageF+2)
     C                   endif
     C                   endif

     C                   eval      nPageF = %scan('\':szURL)
     C                   eval      nPageB = %scan('/':szURL)
     C                   if        nPageB = 0 and nPageF = 0
     C                   eval      nPage = 0
     C                   else
     C                   if        nPageB > nPageF
     C                   eval      nPage = nPageB
     C                   else
     C                   eval      nPage = nPageF
     C                   endif
     C                   endif
     C                   if        nPage > 0
     C                   eval      szDomain = %subst(szURL:1:nPage-1)
     C                   eval      szPage   = %Subst(szURL:nPage)
     C                   else
     C                   eval      szDomain = %trimR(szURL)
     C                   endif

     C                   eval      nBytes=GetUrldata(szDomain:
     C                                              szPage : *OMIT:
     C                                              pHtml:%size(szHtml):
     C                                              0 : 367)

     C* Recuperar los datos que retorna el SW REST y la Longitud de los datos.
     C                   MOVEL     SZHTML        cReturn
     C                   MOVEL     NBYTES        LARGO
      /free
           exsr leer_json;
      /end-free
     C
     c                   return
      /IF NOT DEFINED(*CRTBNDRPG)
     C                   if        nBytes <= nRtnLen
     C                   eval      szRtnHtml = %subst(szHtml:nBytes)
     C                   else
     C                   eval      szRtnHtml = %subst(szHtml:nRtnLen)
     C                   endif
     C                   callp      Joblog('Salida larga %s':szrtnhtml)
      /free
           dsply  szRTNhtml;
      /end-free
     C                   callp      Joblog('Salida CORTA %s':szrtnhtml)
     C                   return    nBytes
      /else
     C                   return
      /endif
      /free

         // Rutina propia para recuperar datos de JSON exclusivo para datos      

         // alfanumericos
         begsr leer_json;



           for nNro = 1 to 30;
               cRet(nNro) = '';   // Encerar variable
           endfor;

           nPosIni  = %scan('200 OK' : cReturn);  // Revisar si existe respuesta
           if nPosIni <= 0;
              nLarJson = 0;
           else;
              nLarJson = 1;
           Endif;
           if nLarJson = 0;
               cRet(01) = '500';
               cRet(02) = 'SERVICIO WEB NO DISPONIBLE';
           else;
               nPosIni  = %scan('{' : cReturn);
               nLargo   = %size(cReturn);
               nLarJson = nLargo - nPosIni;
               cJson    = %subst(cReturn : nPosIni : nLarJson);
               cBuscar  = 'codigoError';
               exsr Busca_error;
               exsr Busca_Json ;
               if %trim(cValor) = '0000';
                  cRet(01) = %trim(cValor);
                  cRet(02) = 'SERVICIO WEB EN LINEA';
               else;
                  cRet(01) = %trim(cValor);
                  cBuscar = 'mensajeError';
                  exsr Busca_error;
                  exsr Busca_Json ;
                  cRet(02) = cValor;
                  nLarJson = 0;
               endIf;
           endIf;
           if  nLarJson > 0;

               nPosIni  = 1;
               nNro     = 3;
               dow nlarjson > 0;
                   exsr Busca_Json;
                   if nError > 0;
                      leave;
                   endif;
                   cRet(nnro) = cValor;
                   exsr Busca_Json;
                   if nError > 0;
                      leave;
                   endif;
                   nNro = nNro + 1;
                   cRet(nNro) = cValor;
                   nNro = nNro + 1;
               enddo;
           Endif;
         endsr;


         // Subrutina para Buscar en la cadena JSON si existe error en la 

         // Recuperacion de datos
         begsr Busca_Error;

            nError   = 0;
            nPosIni  = 1;
            nUbiIni = %scan(%trim(cBuscar) : cJson : nPosIni);
            if nUbiIni = 0;
               nError = 1;
            else;
               nPosIni = nUbiIni + 1 + %len(%trim(cBuscar));
            endif;
         endsr;

         // Subrutina para Buscar en la cadena JSON los datos de etiqueta y valor,
         // funciona bien solo en el caso de DATOS ALFANUMERICOS, cuya forma 
         // simple es {"NombreCliente":"JOSE CANDELARIOS TRES PATINES", 
         // "ClienteCodigo":"95959595"}
         begsr Busca_json;

            nError   = 0;
            nUbiIni = %scan('"' : cJson : nPosIni);
            if nUbiIni = 0;
               nError = 1;
            else;
               nPosFin = nUbiIni + 1;
               nUbiFin = %scan('"' : cJson : nPosFin);
               if nUbiFin = 0;
                  nError = 2;
               else;
                  nPosIni = nUbiFin + 1;
                  nUbiIni = nUbiIni + 1;
                  nUbiFin = nUbiFin;
                  nLarCpo = nUbiFin - nUbiIni;
                  if nLarCpo > 0;
                     cValor  = %subst(cJson : nUbiIni : nLarCpo);
                  else;
                     cValor = '';
                  endif;
               endif;
           endif;
         endsr;
      /end-free



En el ISERIES se puede crear un RPG o CL que invoque a dicho programa de la siguiente forma:
CALL CONSUMESW('09292', '')

y Retornara un Arreglo de la siguiente forma:
Pos-1 0000
Pos-2 SERVICIO WEB DISPONIBLE
Pos-3 Cliente
Pos-4 09292
Pos-5 Nombres
Pos-6 JOSE CANDELARIO TRES PATINES
Pos-7 codigoError
Pos-8 '0000'
Pos-9 mensajeError
Pos-10 'PROCEOS OK'

De esta forma el acceso resulta facil, ya que en RPG realmente es complicado el manejo de cadenas, un poco a mejorado con el RPGLE, pero su editor SEU realmente es muy pobre para este tipo de tareas.

Existe algunas nuevas funciones para ello sugiero siguiente LINK:
Nuevas funciones de RPGLE
Cualquier comentario adicional por favor dejeme hagame conocer  y estaremos en contacto, quiero agradecer a todos ellos que me respondieron a la consulta el HELP400.

CONCLUSIONES: Para aspectos de explotación de la información lo mejor es accesar a ella con cualquier otro lenguaje como Java, CSharp o Python entre los mas utilizados, pero para interconectar el servidor ISERIES y su potente sistema operativo, personalmente creo que vale la pena optar por la idea de consumir servicios web REST o SOAP, con RPGLE, exponer servicios web con RPGLE, de muchas formas no le veo muy eficiente porque es un esfuerzo grande para poder obtener resultados aceptables, creo que los lenguajes de 3ra generacion tienen una series de librerias y funciones que superan en gran medida la funcionalidad del RPGLE, recuerden es una vision muy personal.

No hay comentarios: