Árvore de páginas

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

 

Índice

...


Índice
outlinetruestylenone
exclude.*ndice
style
 
none


Consideraciones Generales

La información contenida en este documento tiene el objetivo de demostrar cómo realizar la integración entre TOTVS Fluig Plataforma y aplicaciones externas. Para una comprensión completa de esta información, algunos conocimientos se consideran requisitos previos, incluyendo entre ellos:

  • Visión general del producto TOTVS Fluig Plataforma
  • Visión general de la integración de sistemas
  • JavaScript
  • WebServices
  • SOAP
  • Progress® 4GL

  • Progress® Open App Server
  • Progress® Open Client for Java
  • Datasets (TOTVS Fluig Plataforma)
  • Java™
  • Apache Flex®

En varias partes de este documento se presentarán fragmentos de código en diferentes lenguajes de programación, con el fin de demostrar el uso de las capacidades de integración de TOTVS Fluig Plataforma. Sin embargo, este documento no pretende capacitar al lector en el uso de estas tecnologías más allá de los fines anteriormente descritos, siendo responsabilidad del lector buscar información en profundidad sobre estos lenguajes.

...

Con la llegada de las tecnologías de Sistema de Información, varios sistemas comenzaron a apoyar estos procesos de negocio, en especial aquellos considerados más críticos para la operación de la empresa. El mejor ejemplo de ello es la adopción de los sistemas ERP que ofrecen soporte a los procesos de varias áreas de la empresa.

TOTVS Fluig Plataforma pretende ser una plataforma agnóstica de gestión de procesos, documentos e identidades a través de una interfaz de comunicación colaborativa. Esto se puede percibir en mayor o menor grado en cada una de sus funcionalidades, desde las más simples (como la colaboración) hasta las más complejas (como DM y BPM).

No obstante, parte de estos procesos dependen en gran medida de los sistemas de información ya existentes en la empresa y por esta razón, la arquitectura de TOTVS Fluig Plataforma está diseñada para permitir la integración a estos sistemas, permitiendo que los procesos modelados tengan mayor valor agregado.

TOTVS Fluig Plataforma permite tanto el acceso por el producto a los sistemas externos (para consultar o alimentar la información) como también posibilita que otros sistemas se conecten para consultar información o para ejecutar operaciones de transacción. 


El principal canal de integración del producto es a través de WebServices, que se están convirtiendo en el estándar más común de integración con cualquier aplicación. A través de ellos, se puede tener acceso a las funcionalidades de TOTVS Fluig Plataforma y ofrecer acceso por medio del producto a aplicaciones externas. Este documento dedica una sección específica a la integración a través de WebServices.

La otra forma de integración es a través de llamadas a Progress® Open AppServer, que se recomienda para usuarios que necesitan integrar TOTVS Fluig Plataforma con aplicaciones desarrolladas en esta plataforma.

...

La integración a través de WebServices utiliza el protocolo SOAP y, al ser un estándar abierto, permite que sistemas desarrollados en plataformas completamente diferentes como Java ™, Microsoft® .Net, C, C ++, PHP, Ruby, Pearl, Python, entre otras, puedan intercambiar información entre sí de manera transparente.

Acceso a WebServices de TOTVS Fluig Plataforma

TOTVS Fluig Plataforma ofrece un conjunto de WebServices que permiten el acceso a la información del producto o la ejecución de tareas, como por ejemplo iniciar solicitudes de procesos. Para tener la lista de los WebServices disponibles, ingrese a: 

...

Nota

Tenga en cuenta que cada tipo de atributo que se espera, por ejemplo el atributo expirationDate del objeto DocumentDto[] es una fecha, pero cada lenguaje lo interpreta de manera diferente, vea a continuación algunos ejemplos:

  • C#: dateTime
  • Java™: XMLGregorianCalendar
  • Progress®: DATETIME-TZ

...


Vía Apache Flex®

Al igual que la mayoría de las herramientas de desarrollo, Apache Flex® permite crear stubs para acceder a web services. Estos stubs encapsulan todas las operaciones de empaquetado y desempaquetado de la información del estándar XML para los tipos nativos de la plataforma.

Utilice el paso a paso para visualizar el proceso de creación de los stubs para un servicio ofrecido por TOTVS Fluig Plataforma:

Deck of Cards
history
historyfalse
idstubFlexfalse
Card
defaulttrue
effectDuration0.5
id1
label1º Paso
effectTypefade

La creación de los stubs en Flex® se lleva a cabo a través del menú Data, opción Import WebService(WSDL), como muestra la siguiente imagen.

Card
effectDuration0.5
id2
label2º Paso
effectTypefade

En la primera ventana, se solicita la carpeta dentro del proyecto actual donde deben generarse los stubs.

Card
effectDuration0.5
id3
label3º Paso
effectTypefade

En la siguiente pantalla, se debe informar la dirección de WSDL donde se encuentra el servicio. También se puede definir si se accede desde la estación de cliente o desde el servidor LiveCycle Data Services.

Card
effectDuration0.5
id4
label4º Paso
effectTypefade

En la última pantalla, se debe informar el package que se utilizará y cuál es el nombre de la clase principal (ya sugeridos por Flex™ Builder™).

Card
effectDuration0.5
id5
labelResultado
effectTypefade

Una vez finalizado el proceso, Flex ™ Builder ™ agregará al proyecto un conjunto de clases que serán utilizadas por el programador para invocar los servicios, como se muestra a continuación:

...

El siguiente fragmento de código presenta un ejemplo de invocación de WebService de acceso a los Datasets de TOTVS Fluig Plataforma:

Bloco de código
languageactionscript3
themeEclipse
languagefirstlineactionscript31
titleECMDatasetServiceClient.mxml
firstline1
linenumberstrue
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="{this.start()}">
	<mx:Script>
		<![CDATA[
			import generated.webservices.ValuesDto;
			import generated.webservices.DatasetDto;
			import generated.webservices.GetDatasetResultEvent;
			import generated.webservices.SearchConstraintDtoArray;
			import generated.webservices.StringArray;
			import generated.webservices.ECMDatasetServiceService;
			import mx.rpc.events.FaultEvent;
			import mx.collections.ArrayCollection;
		
			//Crea una instancia de stub de acceso al servicio
			private var ds:ECMDatasetServiceService = new ECMDatasetServiceService();
			public function start() : void {
				//Crea tipos auxiliares que se utilizarán en la llamada de servicio
				var fields:StringArray = new StringArray();
				var constraints:SearchConstraintDtoArray = new SearchConstraintDtoArray();
				var order:StringArray = new StringArray();
				//Define las funciones para tratamiento de la devolución
				ds.addEventListener(GetDatasetResultEvent.GetDataset_RESULT, resultGetDataset);
				ds.addEventListener(FaultEvent.FAULT,faultGetDataset);
				
				//invoca el método getDataset del servicio
				ds.getDataset("adm", 1, "adm", constraints, order, fields, "colleague");
			}
			
			//Tratamiento de los datos devueltos por el servicio invocado.
			public function resultGetDataset(ev:GetDatasetResultEvent) : void {
				//Recupera la devolución del servicio, en forma de un DatasetDto
				var dataset:DatasetDto = ev.result as DatasetDto;
				//Instala una string con todos los datos del dataset
				var line:String = "";
				
				//Encabezado con el nombre de los campos
				var columnsArray:ArrayCollection = new ArrayCollection(dataset.columns);
				for (var j:int = 0; j < columnsArray.length; j++) {
					line += columnsArray.getItemAt(j) + "\t";
				}
				//Línea de datos
				var valuesArray:ArrayCollection = new ArrayCollection(dataset.values);
				for (var j:int = 0; j < valuesArray.length; j++) {
					var row:ValuesDto = valuesArray.getItemAt(j) as ValuesDto;
					line += "\n" + j + ":";
					
					for (var i:int = 0; i < row.length; i++) {
						line += row.getItemAt(i) + "\t";
					}
				}
				
				//Muestra la string creada en un textarea en la pantalla
				this.sysout.text = line;
			}
			
			public function faultGetDataset(ev:FaultEvent) : void {
				this.sysout.text = ev.fault.faultString;
			}
		]]>
	</mx:Script>
	<mx:TextArea id="sysout" name="sysout" width="100%" height="100%" 
		paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5"/>
</mx:Application>
Nota

Existe un bug de Flex® que impide el funcionamiento correcto de servicios que trabajan con matrices multidimensionales de datos, como en el ejemplo anterior, donde se devuelve un array (de líneas del Dataset) de array (de las columnas de cada registro).

Para solucionar este problema, es necesario cambiar la clase generada por Flex™ Builder™ que encapsulará el array multidimensional. En el ejemplo anterior, esta clase es DatasetDto, que deberá cambiarse (línea 11) como se muestra en el siguiente ejemplo:

Bloco de código
languageactionscript3
themeEclipselanguageactionscript3
firstline1
linenumberstrue
public class DatasetDto
{
	/**
	 * Constructor, initializes the type class
	 */
	public function DatasetDto() {}
            
	[ArrayElementType("String")]
	public var columns:Array;
	[ArrayElementType("ValuesDto")]
	public var values:Array = new Array(); //iniciando el array
}

Otros servicios que no trabajan con arrays multidimensionales no exigen cambios en el código generado.

...


Via Java™

Existen muchas implementaciones de uso de WebServices en Java ™ y en este ejemplo vamos a utilizar las bibliotecas disponibles en Java ™ 7.

...

A partir de los stubs generados, se puede consumir el WebService como se muestra en el siguiente ejemplo:

Bloco de código
languagejava
themeEclipse
languagefirstlinejava1
titleECMDatasetServiceClient.javafirstline1
linenumberstrue
package com.fluig.examples;
import javax.xml.ws.BindingProvider;
import net.java.dev.jaxb.array.StringArray;
import com.totvs.technology.ecm.dataservice.ws.DatasetDto;
import com.totvs.technology.ecm.dataservice.ws.DatasetService;
import com.totvs.technology.ecm.dataservice.ws.ECMDatasetServiceService;
import com.totvs.technology.ecm.dataservice.ws.SearchConstraintDtoArray;
import com.totvs.technology.ecm.dataservice.ws.ValuesDto;
/*
 * Clase para invocar servicio DatasetService
 */
public class ECMDatasetServiceClient {
	//Instancia DatasetServiceService.
	private ECMDatasetServiceService ecmDatasetServiceService = new ECMDatasetServiceService();
	private DatasetService service = ecmDatasetServiceService.getDatasetServicePort();
	
	//Inicia ejecución de la clase
	public static void main(String[] args) {
		ECMDatasetServiceClient client = new ECMDatasetServiceClient();
		
		//Configura acceso a WebServices.
		BindingProvider bp = (BindingProvider) client.service;
		bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, 
				"http://localhost:8080/webdesk/ECMDatasetService");
		try {
			client.getDataset();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public void getDataset() throws Exception {
		
		//Crea los parámetros utilizados en la llamada
		int companyId = 1;
		String username = "adm";
		String password = "adm";
		String name = "colleague";
		StringArray fields = new StringArray();
		SearchConstraintDtoArray constraints = new SearchConstraintDtoArray();
		StringArray order = new StringArray();
		
		//Invoca el servicio de dataset
		DatasetDto result = service.getDataset(
				companyId, username, password, name, fields, constraints, order);
		
		//Presenta el encabezado
		for (String columnName : result.getColumns()) {
			System.out.print(columnName + "\t");
		}
		System.out.println();
		
		//Presenta las líneas del dataset
		for (ValuesDto row : result.getValues()) {
			for (Object value : row.getValue()) {
				System.out.print(value + "\t");
			}
			System.out.println();
		}
	}
}
Nota

Al utilizar los WebServices vía Java ™, se debe prestar atención al tipo de cada atributo y al tipo de devolución de WebService. Por ejemplo, para valores del tipofecha se debe utilizar la clase XMLGregorianCalendar:

Bloco de código
language
languagejava
themeEclipse
javafirstline1
linenumberstrue
DocumentDto document = new DocumentDto();

XMLGregorianCalendar date = DatatypeFactory.newInstance().newXMLGregorianCalendar();
date.setYear(2013);
date.setMonth(10);
date.setDay(16);
date.setHour(0);
date.setMinute(0);
date.setSecond(0);

document.setExpirationDate(date);

 


Vía Progress® 4GL

Al igual que en los ejemplos anteriores, el primer paso para consumir un Webservice en Progress® es utilizar una utilidad que leerá la dirección WSDL y generará la información necesaria para su acceso. A diferencia de Java™ y Flex®, Progress® no genera objetos de stub, pero sí una documentación sobre cómo consumir los servicios descritos en el archivo WSDL. Aunque en algunas situaciones es posible utilizar los tipos nativos de Progress® como parámetros, dependiendo del tipo de dato utilizado es necesario manipular el XML SOAP para extraer o enviar una información.

...

El siguiente código presenta un ejemplo de cómo consumir el servicio:

Bloco de código
firstline
languagejavafx
themeEclipse
languagefirstlinejavafx1
titlewsECMDatasetService.p
1linenumberstrue
/* Parte I - Invocar o WebService */
DEFINE VARIABLE hWebService     AS HANDLE NO-UNDO.
DEFINE VARIABLE hDatasetService AS HANDLE NO-UNDO.

DEFINE VARIABLE cFields  AS CHARACTER EXTENT 0 NO-UNDO.
DEFINE VARIABLE cOrder   AS CHARACTER EXTENT 0 NO-UNDO.
DEFINE VARIABLE cDataset AS LONGCHAR NO-UNDO.

DEFINE TEMP-TABLE item NO-UNDO
    NAMESPACE-URI ""
    FIELD contraintType AS CHARACTER
	FIELD fieldName     AS CHARACTER
	FIELD finalValue    AS CHARACTER
	FIELD initialValue  AS CHARACTER.
 
DEFINE DATASET dConstraints NAMESPACE-URI "http://ws.dataservice.ecm.technology.totvs.com/"
	FOR item.

CREATE SERVER hWebService.
hWebService:CONNECT("-WSDL 'http://localhost:8080/webdesk/ECMDatasetService?wsdl'").
RUN DatasetService SET hDatasetService ON hWebService.

RUN getDataset IN hDatasetService(INPUT 1,
                                  INPUT "adm",
                                  INPUT "adm",
                                  INPUT "colleague",
                                  INPUT cFields,
                                  INPUT DATASET dConstraints,
                                  INPUT cOrder,
                                  OUTPUT cDataset).

DELETE OBJECT hDatasetService.
hWebService:DISCONNECT().
DELETE OBJECT hWebService.

/* Parte II - Faz o parser do XML e criar um arquivo texto separado por tabulacao */
DEFINE VARIABLE iCount  AS INTEGER   NO-UNDO.
DEFINE VARIABLE iCount2 AS INTEGER   NO-UNDO.
DEFINE VARIABLE hDoc    AS HANDLE    NO-UNDO.
DEFINE VARIABLE hRoot   AS HANDLE    NO-UNDO.
DEFINE VARIABLE hValues AS HANDLE    NO-UNDO.
DEFINE VARIABLE hEntry  AS HANDLE    NO-UNDO.
DEFINE VARIABLE hText   AS HANDLE    NO-UNDO.
DEFINE VARIABLE cValue  AS CHARACTER NO-UNDO.

OUTPUT TO c:\dataset.txt.

CREATE X-DOCUMENT hDoc.
CREATE X-NODEREF hRoot.
CREATE X-NODEREF hEntry.
CREATE X-NODEREF hText.
CREATE X-NODEREF hValues.

hDoc:LOAD("longchar", cDataset, FALSE).
hDoc:GET-DOCUMENT-ELEMENT(hRoot).

/* Percorre as colunas <columns> */ 
DO iCount = 1 TO hRoot:NUM-CHILDREN WITH 20 DOWN:
    hRoot:GET-CHILD(hEntry, iCount).
    IF hEntry:NAME <> "columns" THEN
        NEXT.

    hEntry:GET-CHILD(hText, 1).
    PUT UNFORMATTED hText:NODE-VALUE "~t".
    DOWN.
END.
PUT UNFORMATTED SKIP.

/* Percorre os registros <values> */
DO iCount = 1 TO hRoot:NUM-CHILDREN WITH 20 DOWN:
    hRoot:GET-CHILD(hValues, iCount).
    IF hValues:NAME <> "values" THEN
        NEXT.

    /* Percorre os campos <value> */
    DO iCount2 = 1 TO hValues:NUM-CHILDREN:
        hValues:GET-CHILD(hEntry, iCount2).

        IF hEntry:NUM-CHILDREN = 0 THEN
            cValue = "".
        ELSE DO:
            hEntry:GET-CHILD(hText, 1).
            cValue = hText:NODE-VALUE.
        END.
        PUT UNFORMATTED cValue "~t".
    END.

    PUT UNFORMATTED SKIP.
END.

OUTPUT CLOSE.

DELETE OBJECT hValues.
DELETE OBJECT hText.
DELETE OBJECT hEntry.
DELETE OBJECT hRoot.
DELETE OBJECT hDoc.

Acceso a WebServices desde TOTVS Fluig Plataforma

TOTVS Fluig Plataforma permite realizar llamadas a WebServices de terceros a través del registro de Servicios en la visualización de Servicios de Fluig Studio.

...

Basado en esta información, TOTVS Fluig Plataforma extraerá la información sobre el WebService informado y finalizará el registro de este servicio. 

Una vez que el servicio está registrado, se pueden ver las clases y métodos disponibles en este servicio y que se utilizarán en los códigos JavaScript que lo usarán. La siguiente pantalla presenta un ejemplo de visualización de WebService.

 


Los servicios agregados en TOTVS Fluig Plataforma se pueden instanciar y utilizar en los puntos donde el producto permite personalización utilizando JavaScript, como en scripts para eventos globales, eventos de procesos, eventos de definición de formulario o Datasets. En el siguiente ejemplo se creará un Dataset que usará este servicio para traer los datos de la tabla periódica.

El siguiente código presenta una implementación de ejemplo del uso de un servicio en la construcción de un Dataset:

Bloco de código
languagejavascript
themeEclipse
languagefirstlinejavascript1
titleperiodicTable.jsfirstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {
	//Crea el dataset
	var dataset = DatasetBuilder.newDataset();
	dataset.addColumn("elementName");
	// Conecta el servicio y busca los libros
	var periodicService = ServiceManager.getService('PeriodicTable');
	var serviceHelper = periodicService.getBean();
	var serviceLocator = serviceHelper.instantiate('net.webservicex.Periodictable');
	var service = serviceLocator.getPeriodictableSoap();
	//Invoca el servicio
	try {
		var result = service.getAtoms();
		var NewDataSet = new XML(result);
		for each(element in NewDataSet.Table) {
			dataset.addRow(new Array(element.ElementName.toString()));
		}
	} catch(erro) {
		dataset.addRow(new Array(erro));
	}
	return dataset;
}

El primer paso para invocar el servicio es solicitar a TOTVS Fluig Plataforma que cargue el servicio, a partir del método ServiceManager.getService('PeriodicTable'). El valor informado como parámetro debe ser el código utilizado cuando se registró el servicio.

...

Para el servicio de la tabla periódica es necesario realizar los siguientes pasos:

Bloco de código
languagejavascript
themeEclipselanguagejavascript
firstline1
linenumberstrue
var serviceLocator = serviceHelper.instantiate('net.webservicex.Periodictable');
var service = serviceLocator.getPeriodictableSoap();
var result = service.getAtoms();

...

En el caso de este servicio, el método getAtoms devuelve una string que contiene un XML con la lista de todos los elementos, como se muestra en el siguiente ejemplo:

Eclipse
Bloco de código
theme
languagehtml/xml
themeEclipse
firstline1
linenumberstrue
<NewDataSet>
	<Table>
		<ElementName>Actinium</ElementName>
	</Table>
	<Table>
		<ElementName>Aluminium</ElementName>
	</Table>
	...
</NewDataSet>

...

El siguiente ejemplo presenta el código utilizado para recorrer el XML devuelto:

Bloco de código
languagejavascript
themeEclipse
languagejavascript
firstline1
linenumberstrue
var NewDataSet = new XML(result);
for each(element in NewDataSet.Table) {
	dataset.addRow(new Array(element.ElementName.toString()));
}

Una vez implementado el código del Dataset, es posible visualizarlo, como muestra la siguiente figura:

 


WebServices con Autenticación Básica

Para consumir WebServices que utilizan la autenticación básica (WSS o WS-Security), es necesario utilizar el método getBasicAuthenticatedClient localizado en provider del servicio (el mismo que se obtiene vía ServiceManager). Este método proporciona un client autenticado. 


Los parámetros que se deben informar en el método siguen el siguiente orden:

...

Utilizando el ejemplo del servicio PeriodicTable presentado anteriormente, el código de la llamada tendría los siguientes cambios:

Bloco de código
language
languagejavascript
themeEclipse
javascriptfirstline1
linenumberstrue
var serviceLocator = serviceHelper.instantiate('net.webservicex.Periodictable');
var service = serviceLocator.getPeriodictableSoap();
var authenticatedService = serviceHelper.getBasicAuthenticatedClient(service, "net.webservicex.PeriodictableSoap", 'usuario', 'senha');
var result = authenticatedService.getAtoms();

...

Nota
titleAtención

Esta técnica es válida para Fluig atualización 1.3.7 o superior.

En integraciones que utilizan los servicios creados con CXF con sistemas que no soportan el protocolo HTTP/1.1 (Protheus, por ejemplo), se debe utilizar este método configurando el parámetro "disable.chunking" con el valor "true".

Para personalizar el client que accede al servicio se debe utilizar el método getCustomClient, localizado en provider del servicio (el mesmo que se obtiene vía ServiceManager). Esta configuración exige la creación de un mapa de parámetros con sus respectivos valores para pasar al método, según el siguiente snippet:

Bloco de código
languagejs
themeEclipselanguagejs
		var properties = {};
		properties["basic.authorization"] = "true";
		properties["basic.authorization.username"] = "username";
		properties["basic.authorization.password"] = "password";
		properties["disable.chunking"] = "true";
		properties["log.soap.messages"] = "true";
		
		var serviceLocator = serviceHelper.instantiate('net.webservicex.Periodictable');
		var service = serviceLocator.getPeriodictableSoap();
		var customClient = serviceHelper.getCustomClient(service, "net.webservicex.PeriodictableSoap", properties);
		var result = customClient.getAtoms();

...


Los parámetros que se pueden configurar son los siguientes:

Propriedad
Función
basic.authorization

Cuando se define como "true", hace lo mismo que el método getBasicAuthenticatedClient, pero permite aplicar la configuración de autenticación junto con las demás personalizaciones que se detallan a continuación. Para configurar la autenticación, las siguientes propiedades con "username" y "password" también necesitan definirse.

basic.authorization.username
Usuario que se utilizará para autenticación básica.
basic.authorization.password
Contraseña de usuario utilizado para autenticación básica.
disable.chunking

Cuando se define como "true", desactiva el envío de solicitudes grandes en "fragmentos" menores. Puede ser útil cuando el servicio llamado no admite este tipo de solicitud.

log.soap.messages
Cuando se define como "true", permite que los mensajes SOAP utilizados en las solicitudes realizadas a los servicios se presenten en el log del servidor, lo que facilita la depuración en caso de fallas.

Progress® Open AppServer

 


Así como es posible invocar operaciones en WebServices, TOTVS Fluig Plataforma también permite realizar llamadas a programas en Progress® 4GL (o ABL) expuestos vía Progress® Open AppServer.

...

Tenga en cuenta que los ejemplos que aquí se presentan pretenden demostrar la dinámica de la integración entre Progress® y TOTVS Fluig Plataforma sin entrar en detalles específicos de las tecnologías involucradas. La capa de servicios Progress® de TOTVS Fluig Plataforma crea una interfaz en JavaScript para la biblioteca Java Open AppServer Client, de Progress® y, por lo tanto, para más información sobre cómo integrar aplicaciones Java ™ y Progress® vea la documentación suministrada por Progress®.

...

Los ejemplos que se muestran a continuación, tienen como objetivo crear cuatro Datasets 1 en TOTVS Fluig Plataforma:

  1. Tipos de Centro de Costo, que deben devolver los tipos de centros de coste existente en la aplicación en Progress® (en este caso, EMS2).
  2. Naturaleza de los Centros de Costo, que debe devolver los tipos posibles de naturaleza, según la aplicación en Progress® (en este caso, EMS2).
  3. Centros de Costo, que debe devolver los registros en la tabla cuenta 2.
  4. Usuarios Comunes, que debe generar una lista de usuarios comunes entre TOTVS Fluig Plataforma y la aplicación en Progress® (utilizando la tabla usuar_mestre)

     


    Nota

    1 - Los ejemplos utilizan una base de datos de EMS2 para consultar los centros de costo y los usuarios. No obstante, sólo dos tablas y seis campos se utilizan en total, lo que no debe perjudicar la comprensión de la lógica por parte del lector, ni impedir la creación de un esquema equivalente para pruebas, en caso de ser necesario.

    2 - El código presentado para extracción de los centros de costo tiene fines puramente educativos y no puede considerarse para el uso en producción. Para obtener más información acerca de cómo extraer los centros de costos de EMS2, consulte su documentación técnica.

Para los tres primeros casos, la lógica de extracción de la información deseada estará expuesta en un programa con varios procedures, uno para cada necesidad que aquí se presenta:

Bloco de código
languagejavafx
themeEclipse
languagefirstlinejavafx1
titleCostCenterUtils.pfirstline1
linenumberstrue
/**************************************************************************
** Utilidad que ofrece procedures para la extracción de información
** sobre centros de costo.
**************************************************************************/
DEFINE TEMP-TABLE ttCC NO-UNDO
    FIELD cuenta    LIKE cuenta.ct-codigo /* CHARACTER */
    FIELD naturaleza LIKE cuenta.naturaleza  /* INTEGER   */
    FIELD tipo     LIKE cuenta.tipo      /* INTEGER   */
    FIELD titulo   LIKE cuenta.titulo.   /* CHARACTER */
 
/*-------------------------------------------------------------------
  Procedure: readCostCenters
   Objetivo: Devuelve una temp-table con la lista de centros de costo.
----------------------------------------------------------------------*/
PROCEDURE readCostCenters:
    DEFINE OUTPUT PARAMETER TABLE FOR ttCC.
    FOR EACH cuenta:
        CREATE ttCC.
        ASSIGN
            ttCC.cuenta    = cuenta.ct-codigo
            ttCC.naturaleza = cuenta.naturaleza
            ttCC.tipo     = cuenta.tipo
            ttCC.titulo   = cuenta.titulo.
    END.
END.
/*-------------------------------------------------------------------
  Procedure: readCostNatureTypes
   Objetivo: Devuelve una string con las naturalezas de los centros de costo,
             separadas por coma.
----------------------------------------------------------------------*/
PROCEDURE readCostNatureTypes:
    DEFINE OUTPUT PARAMETER cNatureList AS CHARACTER NO-UNDO.
    cNatureList = {adinc/i01ad047.i 03}.
END.
/*-------------------------------------------------------------------
  Procedure: readCostTypes
   Objetivo: Devuelve una string con los tipos de centro de costo,
             separados por coma.
----------------------------------------------------------------------*/
PROCEDURE readCostTypes: 
    DEFINE OUTPUT PARAMETER cTypeList   AS CHARACTER NO-UNDO.
    cTypeList = {adinc/i02ad047.i 3}.
END.

En el caso de la extracción de usuarios comunes a los dos productos, se utilizará un único programa, según el siguiente código:

Bloco de código
languagejavafx
themeEclipse
languagefirstlinejavafx1
titleverifyUsers.p
firstline1
linenumberstrue
/**************************************************************************
** Utilidad que recibe una temp-table con una lista de usuarios y devuelve
** otra, sólo con los usuarios de la lista que existan en la base de datos.
**************************************************************************/
DEFINE TEMP-TABLE ttUsers
    FIELD cod_usuar   AS CHARACTER
    FIELD nom_usuario AS CHARACTER
    INDEX principal	  IS PRIMARY UNIQUE cod_usuar.
	
DEFINE TEMP-TABLE ttOutUsers LIKE ttUsers.
DEFINE INPUT  PARAMETER TABLE FOR ttUsers.
DEFINE OUTPUT PARAMETER TABLE FOR ttOutUsers.
FOR EACH ttUsers:
   IF CAN-FIND(usuar_mestre WHERE usuar_mestre.cod_usuar = ttUsers.cod_usuar) THEN DO:
        CREATE ttOutUsers.
        BUFFER-COPY ttUsers TO ttOutUsers.
    END.
END.

...

Utilice el paso a paso para visualizar el proceso de creación del proxy: 


Deck of Cards
history
startHiddenfalse
effectDuration0.5
historyfalse
idproxyGen
falseeffectTypefade
Card
defaulttrue
id1
label1º Passo
  • En la primera pantalla del ProxyGen, el punto principal que se debe tener en cuenta es el nombre del Proyecto (en el ejemplo, EMSProxies). La información de este campo será utilizada por ProxyGen para nombrar la clase de acceso al servicio, y se utilizará en la configuración del servicio en Fluig. En esta pantalla también es necesario configurar PROPATH de manera correcta, para que se puedan encontrar los archivos compilados (.r).

Card
id2
label2º Passo


 

  • El segundo paso consiste en introducir los procedures que se expondrán de forma persistente o no persistente. La elección de la opción que se va a utilizar depende de la forma en la que se construyó cada objeto expuesto. Después de introducidos los procedures, haga clic en la opción Generate.

 


Card
id3
label3º Passo


 

  • Durante el proceso de generación de proxy, en la pestaña General, marque la opción Java en Client Proxy e ingresar el directorio en el que se generará el proxy en Output Dir. Observe también el campo AppService, éste debe ser el nombre del servicio publicado en AppServer, de lo contrario no se podrá conectar al servidor.

 


 
Card
id4
label4º Passo


  • La última información relevante para la generación de proxy es el nombre del paquete (package) donde se crearán las clases. Esta información se utiliza durante la configuración del servicio Progress® en Fluig. Para finalizar haga clic en el botón OK.

  


Card
id5
labelÚltimo Passo


 

  • Una vez creadas las clases, es necesario empaquetarlas en un archivo .JAR. Esto se puede realizar por línea de comando, utilizando el siguiente comando:

Sem Formato
jar -cvf <jar_file_name> <directorio>

 


Sólo tenga en cuenta que en el archivo generado, es necesario que las clases estén en los directorios correctos. En el ejemplo presentado, el directorio con debe incluirse y estar en la raíz del archivo JAR. Por ser compatible con el formato ZIP, otra opción es generar un archivo con las clases generadas (respetando los directorios) y renombrarlo con la extensión .JAR. 


Informações

Dependiendo de la versión de Progress®, las pantallas pueden sufrir alguna variación en la cantidad y disposición de los campos. En caso de dudas consulte la documentación

 

...



Configuración del Servicio en Fluig

El registro de un servicio se realiza a través de Fluig Studio, en la view Visualización de Servicios, en la opción  Incluir Servicio. La siguiente pantalla muestra el asistente del nuevo servicio y los campos utilizados para el registro del servicio Progress®:

 


 


  • Donde:

    • ServidorServidor de Fluig donde se agregará el servicio;
    • Código: Código único que identificará el servicio en el sistema. Este código se utilizará en los códigos JavaScript para acceder a este servicio;
    • Descripción: Texto que describe el servicio de datos;
    • URL: Identifica la URL de acceso al servicio AppServer, como por ejemplo AppServer://<servidor>/<nome_serviço>;
      En caso de que no esté utilizando el NameServer estándar se debe informar el puerto de acceso a NameServer Ej.: AppServer://<servidor>:<porta_NameServer>/<nome_serviço>. 
      Tenga en cuenta que dependiendo de la configuración del servicio y/o de la forma de conexión, la URL puede sufrir cambios. Verifique la documentación de Open AppServer para más información.
    • Tipo: Identifica el tipo de servicio (Progress o WebService). Se debe seleccionar Progress;
    • Objeto Remoto: Identifica la clase de acceso de proxy. Esta clase generalmente está compuesta por el nombre del paquete utilizado para generar las clases Proxy, más el nombre del proyecto en ProxyGen. 
      Ejemplo: En las pantallas que presentan ProxyGen, se utilizó el paquete com.fluig.samples.ems, y el nombre que se dio al proyecto en ProxyGen fue EMSProxies. En este caso, la clase del objeto remoto será com.fluig.samples.ems.EMSProxies;
    • Usuario: Usuario utilizado para conectarse al servicio, como se define en la configuración de AppServer;
    • Contraseña: Contraseña utilizada para conectarse al servicio, como se define en la configuración de AppServer;
    • Parámetros Extras: Parámetros extras (y opcionales) utilizados para conectarse a AppServer. Verifique la documentación de Open AppServer para verificar las opciones disponibles en cada versión de Progress®;
    • Directorio del archivo de Proxy: Archivo .JAR que contiene las clases generadas por ProxyGen. Se debe utilizar el botón Seleccionar Archivo para localizarlo.


...

El acceso a los procedures expuestos en AppServer implica cuatro elementos que interactúan entre sí de acuerdo al siguiente diagrama:

 


 


Donde:

  • Script Code: es el código en JavaScript que hará uso de los procedures expuestos en AppServer. Como se mencionó anteriormente, este JavaScript puede ser de cualquier naturaleza, tales como la implementación de un Dataset o la personalización de un evento de proceso.
  • Service Provider: Objeto recuperado a través del método ServiceManager.getService que proporciona el acceso a las funcionalidades del servicio. Este objeto se encarga de gestionar la conexión y los recursos asignados por el servicio durante la ejecución del script.
  • Service Helper: Objeto recuperado a través del método getBean en ServiceProvider que pone a disposición un conjunto de métodos de utilidad que permiten, entre otras cosas, crear objetos específicos de Progress® (StringHolder, ResultSetHolder, etc.), tener acceso al objeto remoto de ProxyGen e instanciar clases. Para más información sobre Service Helper consulte aquí.
  • ProxyGen Clases: Clases generadas por ProxyGen y serán utilizadas por el desarrollador para la ejecución de las rutinas en Progress®. La lista de las clases disponibles, así como sus métodos, se pueden ver en la Visualización de Servicios de Fluig Studio.

...


Procedures Persistentes y No Persistentes

...

Los procedures expuestos de forma persistente dan origen a nuevas clases que pueden instanciarse a través de llamadas a métodos en el Objeto Remoto (a través de la Visualización de Servicios en Fluig Studio se pueden verificar los métodos disponibles en la clase), o por el método createManagedObject. La llamada a través del método createManagedObject permite que  Fluig tenga control sobre el ciclo de vida de este objeto, liberándolo de forma automática al final del método. Si el objeto es instanciado de forma manual, el desarrollador debe codificar la liberación del objeto (método _release()), bajo pena de bloquear un nuevo agente de AppServer en cada invocación del método. 


Parámetros de Entrada y Salida

...

Como ya se ha visto anteriormente, los Datasets que se presentarán aquí son Tipos de Centro de CostoNaturaleza de los Centros de CostoCentros de Costo yUsuarios en Común. 


Tipos de Centro de Costo

El siguiente código presenta la implementación del Dataset de Tipos de Centro de Costo. La explicación de cada paso de la implementación se presentará después del código:

Bloco de código
languagejavascript
themeEclipse
languagefirstlinejavascript1
titledsTipoCentroCusto.js
firstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {
	
	//Paso 1 - Crea el dataset
	var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("id");
    dataset.addColumn("descripcion");
    
	//Paso 2 - Invoca el servicio registrado en Fluig
	var servico = ServiceManager.getService("EMS2");
 
	//Paso 3 - Carga el objeto utilidad para integración con Progress
    var serviceHelper = servico.getBean();
 
	//Paso 4 - Carga el procedure persistente CostCenterUtils.p
    var remoteObj = serviceHelper.createManagedObject("CostCenterUtils");
	//Paso 5 - Invoca el procedure que devuelve una string con los tipos de CC
    var types = serviceHelper.createStringHolder();
    remoteObj.readCostTypes(types);
	//Paso 6 - Rompe la string en un array con cada uno de los tipos
    var typeArray = types.getStringValue().split(",");
 
	//Paso 7 - Agrega cada tipo devuelto
    for(var pos = 0; pos < typeArray.length; pos++) {
        dataset.addRow(new Array(pos + 1, typeArray[pos]));
    }
    return dataset;
}

...


Donde:

  • Paso 1: Crea el dataset y agrega los campos del mismo;
  • Paso 2: La invocación del servicio registrada en Fluig se realiza por el método ServiceManager.getService, y el valor pasado como parámetro debe ser el código del servicio. Tenga en cuenta que en este punto no es necesario informar ningún parámetro de conexión al servicio, puesto que esto ya se realizó en su registro;
  • Paso 3: Utiliza el método getBean para devolver un objeto utilidad para servicios Progress®. Esta utilidad proporciona una serie de métodos que facilitan la interacción con el proxy generado y sus métodos se presentarán con más detalle, más adelante en este documento;
  • Paso 4: Realiza la carga del objeto CostCenterUtils de manera gestionada, a través del método createManagedObject de la utilidad creada anteriormente;
  • Paso 5: Invoca el método deseado, en este caso readCostTypes, pasando un StringHolder que recibirá el valor de salida;
  • Paso 6: Transforma la string recibida como parámetro en un array con las opciones. El carácter , (como) se utiliza para determinar los puntos de ruptura de string;
  • Paso 7: Recorre el array creado, agregando una línea en el Dataset para cada ítem del array.  


La siguiente pantalla presenta la visualización de los datos del Dataset creado:

...

El Dataset de Naturaleza de los Centros de Costo es muy similar al Dataset de tipo de centros de costo. En la práctica, el único cambio es que el procedure se llama:

Bloco de código
languagejavascript
themeEclipse
languagefirstlinejavascript1
titledsNaturezaCentroCusto.js
firstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {

	var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("id");
    dataset.addColumn("descricao");

	var servico = ServiceManager.getService("EMS2");
	var serviceHelper = servico.getBean();
	var remoteObj = serviceHelper.createManagedObject("CostCenterUtils");

	var types = serviceHelper.createStringHolder();
	remoteObj.readCostNatureTypes(types);

    var typeArray = types.getStringValue().split(",");

    for(var pos = 0; pos < typeArray.length; pos++) {
        dataset.addRow(new Array(pos + 1, typeArray[pos]));
    }

    return dataset;
}

 


Después del registro del Dataset, se puede ver su contenido:

 


Centros de Costo

El Dataset de Centro de Costo posee una estructura muy similar a la de los dos Dataset visto anteriormente. La principal diferencia es que, en este caso, el procedure devuelve una temp-table con los centros de costos, lo que cambia la forma en la que se manipulan los datos.

...

Las temp-table en Progress® 9 son manejadas por los objetos que implementan la interfaz java.sql.ResultSet:

Bloco de código
languagejavascript
themeEclipse
languagefirstlinejavascript1
titledsCentroCustoP9.jsfirstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {
    
	//Crea la estructura del Dataset
    var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("cuenta");
    dataset.addColumn("titulo");
    dataset.addColumn("naturaleza");
    dataset.addColumn("tipo");
    
	//Recupera el servicio y carga el objeto remoto
    var servico = ServiceManager.getService("EMS2");
    var serviceHelper = servico.getBean();
    var remoteObj = serviceHelper.createManagedObject("CostCenterUtils");
    
    //Lee las cuentas corrientes
    var holder = serviceHelper.createResultSetHolder();
    remoteObj.readCostCenters(holder);
    
    //Recorre los registros, cargando el Dataset con los datos
    var rs = holder.getResultSetValue();
    while (rs.next()) {
        var cuenta 	 = rs.getObject("cuenta");
        var naturaleza = rs.getObject("naturaleza");
        var tipo 	 = rs.getObject("tipo");
        var titulo   = rs.getObject("titulo");
	
        dataset.addRow(new Array(cuenta, titulo, naturaleza, tipo));
    }
    
    return dataset;
}

...

En OpenEdge® 10, las temp-tables devueltas se encapsulan como objetos de la clase ProDataGraph. Esta clase también se utiliza cuando se usan parámetros del tipo DATASET:

Bloco de código
languagejavascript
themeEclipse
languagefirstlinejavascript1
titledsCentroCustoOE10.js
firstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {
    
	//Crea la estructura del Dataset
    var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("cuenta");
    dataset.addColumn("titulo");
    dataset.addColumn("naturaleza");
    dataset.addColumn("tipo");
    
	//Recupera el servicio y carga el objeto remoto
    var servico = ServiceManager.getService("EMS2");
    var serviceHelper = servico.getBean();
    var remoteObj = serviceHelper.createManagedObject("CostCenterUtils");
    
    //Lee las cuentas corrientes
    var holder = serviceHelper.createProDataGraphHolder();
    remoteObj.readCostCenters(holder);
    
	//Recorre los registros, cargando el Dataset con los datos
    var ttCC = holder.getProDataGraphValue().getProDataObjects("ttCC");
    for (var row_index = 0; row_index < ttCC.size(); row_index++) {
        var row = ttCC.get(row_index);
        dataset.addRow(new Array(row.get("cuenta"),
                                 row.get("titulo"),
                                 row.get("naturaleza"),
                                 row.get("tipo")));
    }
    
    return dataset;
}

 


Visualización del Dataset:

 


Usuarios en Común

La primera diferencia entre el Dataset de usuarios comunes y los ejemplos anteriores, es que en este caso es necesario pasar una temp-table como parámetro al procedure invocado.

...

Codificación Progress® 9
Bloco de código
languagejavascript
themeEclipse
languagefirstlinejavascript1
titledsUsuariosComunsP9.jsfirstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {
    
    //Crea el nuevo Dataset
    var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("usuario");
    dataset.addColumn("nombre");
    
	//Recupera los usuarios de TOTVS Fluig Plataforma
    var campos = new Array("colleaguePK.colleagueId", "colleagueName");
    var colleaguesDataset = DatasetFactory.getDataset("colleague", campos, null, null);
    
    //Instancia el servicio
    var servico = ServiceManager.getService("EMS2");
    var serviceHelper = servico.getBean();
    
    //Transforma el dataset en un ResultSet (v9) y crea holder de salida
    var inputTT = colleaguesDataset.toResultSet();
    var holder = serviceHelper.createResultSetHolder();
    
    //Invoca el procedure en el Progress
    serviceHelper.getProxy().verifyUsers(inputTT, holder);
    
    var rs = holder.getResultSetValue();
    while (rs.next()) {
        dataset.addRow(new Array(rs.getObject("cod_usuar"), rs.getObject("nom_usuario")));
    }
    
    return dataset;
}
Codificación OpenEdge® 10
Bloco de código
firstline
languagejavascript
themeEclipse
languagefirstlinejavascript1
titledsUsuariosComunsOE10.js
1linenumberstrue
function createDataset(fields, constraints, sortFields) {
    
	//Crea el nuevo Dataset
    var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("usuario");
    dataset.addColumn("nombre");
    
	//Recupera los usuarios de TOTVS Fluig Plataforma
    var campos = new Array("colleaguePK.colleagueId", "colleagueName");
    var colleaguesDataset = DatasetFactory.getDataset("colleague", campos, null, null);
    
    //Instancia el servicio
    var servico = ServiceManager.getService("EMS2");
    var serviceHelper = servico.getBean();
	
    //Transforma el dataset en un ProDataGraph (v10) y crea holder de salida
    var inputTT = serviceHelper.toProDataGraph(colleaguesDataset);
    var holder = serviceHelper.createProDataGraphHolder();
    
    //Invoca el procedure en el Progress
    serviceHelper.getProxy().verifyUsers(inputTT, holder);
    
    var ttCC = holder.getProDataGraphValue().getProDataObjects("ttOutUsers");
    for (var row_index = 0; row_index < ttCC.size(); row_index++) {
        var row = ttCC.get(row_index);
        dataset.addRow(new Array(row.get("cod_usuar"), row.get("nom_usuario")));
    }
    
    return dataset;
}

 


Visualización del Dataset:

 


Service Helper

La siguiente tabla presenta la lista de métodos existentes en la clase utilidad para servicios Progress®:

Devolución
Método y Descripción
java.lang.ObjectcreateBigDecimalHolder()
Crea un objeto Holder para el tipo DECIMAL
java.lang.ObjectcreateBooleanHolder()
Crea un objeto Holder para el tipo LOGICAL
java.lang.ObjectcreateByteArrayHolder()
Crea un objeto Holder para el tipo RAW
java.lang.ObjectcreateCOMHandleHolder()
Crea un objeto Holder para el tipo COM-HANDLE
java.lang.ObjectcreateDateHolder()
Crea un objeto Holder para el tipo DATE
java.lang.ObjectcreateHandleHolder()
Crea un objeto Holder para el tipo WIDGET-HANDLE (Handle)
java.lang.ObjectcreateIntHolder()
Crea un objeto Holder para el tipo INTEGER
java.lang.ObjectcreateLongHolder()
Crea un objeto Holder para el tipo RECID
java.lang.ObjectcreateManagedObject(java.lang.String objName)
Lee un archivo .p o .r que haya sido expuesto a través de AppServer de forma persistente. A través de este método el proveedor del servicio puede administrar el ciclo de vida de estos objetos, liberándolos al final de la ejecución del script.
java.lang.ObjectcreateMemptrHolder()
Crea un objeto Holder para el tipo MEMPTR
java.lang.ObjectcreateProDataGraph(java.lang.Object metadata)
Crea un objeto de la clase ProDataGraph
java.lang.ObjectcreateProDataGraphHolder()
Crea un objeto Holder para el tipo ProDataGraphHolder
java.lang.ObjectcreateProDataGraphMetaData(java.lang.String name)
Crea un objeto de la clase ProDataGraphMetadata
java.lang.ObjectcreateProDataObjectMetaData(java.lang.String tableName, int numFields, boolean bimageFlag, int numIndexes, java.lang.String multiIxCols, java.lang.String XMLNamespace, java.lang.String XMLPrefix)
Crea un objeto de la clase ProDataObjectMetadata.
Crea un objeto para un dataset (Temp-table).
java.lang.ObjectcreateResultSetHolder()
Crea un objeto Holder para el tipo TABLE
java.lang.ObjectcreateRowidHolder()
Crea un objeto Holder para el tipo ROWID
java.lang.ObjectcreateStringHolder()
Crea un objeto Holder para el tipo CHARACTER.
java.lang.ObjectgetProxy()
Devuelve la instancia del objeto de conexión a AppServer, ya conectado y disponible para su uso. El objeto remoto es la principal clase generada por ProxyGen.
java.lang.Objectinstantiate(java.lang.String className)
Instancia un objeto de una clase dentro de la biblioteca de proxy.
java.lang.ObjecttoProDataGraph(com.datasul.technology.webdesk.dataset.DefaultDataset d)
Transforma un dataset en un ProDataGraph.