Histórico da Página
HTML |
---|
<div id="main-content" class="wiki-content group">
<h1 id="IntegraçãoComAplicativosExternos-Índice">Índice</h1>
<p> </p>
<p>
<style type='text/css'>/*<![CDATA[*/
div.rbtoc1412695546456 {
padding: 0px;
}
div.rbtoc1412695546456 ul {
list-style: none;
margin-left: 0px;
}
div.rbtoc1412695546456 li {
margin-left: 0px;
padding-left: 0px;
}
/*]]>*/
</style>
<div class='toc-macro rbtoc1412695546456'>
<ul class='toc-indentation'>
<li><span class='TOCOutline'>1</span> <a
href='#IntegraçãoComAplicativosExternos-ConsideraçõesGerais'>Consideraciones
Generales</a></li>
<li><span class='TOCOutline'>2</span> <a
href='#IntegraçãoComAplicativosExternos-VisãoGeral'>Visión
General</a></li>
<li><span class='TOCOutline'>3</span> <a
href='#IntegraçãoComAplicativosExternos-WebServices'>WebServices</a>
<ul class='toc-indentation'>
<li><span class='TOCOutline'>3.1</span> <a
href='#IntegraçãoComAplicativosExternos-AcessandoosWebServicesdoFluig'>Acceso
a WebServices de Fluig</a>
<ul class='toc-indentation'>
<li><span class='TOCOutline'>3.1.1</span> <a
href='#IntegraçãoComAplicativosExternos-ViaApacheFlex®'>Vía
Apache Flex®</a></li>
<li><span class='TOCOutline'>3.1.2</span> <a
href='#IntegraçãoComAplicativosExternos-ViaJava™'>Vía Java™</a></li>
<li><span class='TOCOutline'>3.1.3</span> <a
href='#IntegraçãoComAplicativosExternos-ViaProgress®4GL'>Vía
Progress® 4GL</a></li>
</ul></li>
<li><span class='TOCOutline'>3.2</span> <a
href='#IntegraçãoComAplicativosExternos-AcessandoWebServicesapartirdoFluig'>Acceso
a WebServices desde Fluig</a>
<ul class='toc-indentation'>
<li><span class='TOCOutline'>3.2.1</span> <a
href='#IntegraçãoComAplicativosExternos-WebServicescomAutenticaçãoBásica'>WebServices
con Autenticación Básica</a></li>
<li><span class='TOCOutline'>3.2.2</span> <a
href='#IntegraçãoComAplicativosExternos-WebServicecomclientcustomizado'>WebService
con cliente personalizado</a></li>
</ul></li>
<li><span class='TOCOutline'>3.3</span> <a
href='#IntegraçãoComAplicativosExternos-CasodeUso'>Caso de Uso</a></li>
<li><span class='TOCOutline'>3.4</span> <a
href='#IntegraçãoComAplicativosExternos-ConfiguraçãodoAppServer'>Configuración
de AppServer</a></li>
<li><span class='TOCOutline'>3.5</span> <a
href='#IntegraçãoComAplicativosExternos-Expondocódigos4GLcomProxyGen'>Exponiendo
códigos 4GL con ProxyGen</a></li>
<li><span class='TOCOutline'>3.6</span> <a
href='#IntegraçãoComAplicativosExternos-ConfiguraçãodoServiçonoFluig'>Configuración
del Servicio en Fluig</a></li>
<li><span class='TOCOutline'>3.7</span> <a
href='#IntegraçãoComAplicativosExternos-VisãoGeraldosObjetosEnvolvidos'>Visión
general de los objetos involucrados</a>
<ul class='toc-indentation'>
<li><span class='TOCOutline'>3.7.1</span> <a
href='#IntegraçãoComAplicativosExternos-ProceduresPersistenteseNão-Persistentes'>Procedures
Persistentes y No Persistentes</a></li>
<li><span class='TOCOutline'>3.7.2</span> <a
href='#IntegraçãoComAplicativosExternos-ParâmetrosdeEntradaeSaída'>Parámetros
de Entrada y Salida</a></li>
</ul></li>
<li><span class='TOCOutline'>3.8</span> <a
href='#IntegraçãoComAplicativosExternos-CriaçãodosDatasets'>Creación
de los Datasets</a>
<ul class='toc-indentation'>
<li><span class='TOCOutline'>3.8.1</span> <a
href='#IntegraçãoComAplicativosExternos-TiposdeCentrodeCusto'>Tipos
de Centro de Costo</a></li>
<li><span class='TOCOutline'>3.8.2</span> <a
href='#IntegraçãoComAplicativosExternos-NaturezadosCentrosdeCusto'>Naturaleza
de los Centros de Costo</a></li>
<li><span class='TOCOutline'>3.8.3</span> <a
href='#IntegraçãoComAplicativosExternos-CentrosdeCusto'>Centros
de Costo</a>
<ul class='toc-indentation'>
<li><span class='TOCOutline'>3.8.3.1</span> <a
href='#IntegraçãoComAplicativosExternos-CodificaçãoProgress®9'>Codificación
Progress® 9</a></li>
<li><span class='TOCOutline'>3.8.3.2</span> <a
href='#IntegraçãoComAplicativosExternos-CodificaçãoOpenEdge®10'>Codificación
OpenEdge® 10</a></li>
</ul></li>
<li><span class='TOCOutline'>3.8.4</span> <a
href='#IntegraçãoComAplicativosExternos-UsuáriosemComum'>Usuarios
en Común</a>
<ul class='toc-indentation'>
<li><span class='TOCOutline'>3.8.4.1</span> <a
href='#IntegraçãoComAplicativosExternos-CodificaçãoProgress®9.1'>Codificación
Progress® 9</a></li>
<li><span class='TOCOutline'>3.8.4.2</span> <a
href='#IntegraçãoComAplicativosExternos-CodificaçãoOpenEdge®10.1'>Codificación
OpenEdge® 10</a></li>
</ul></li>
</ul></li>
<li><span class='TOCOutline'>3.9</span> <a
href='#IntegraçãoComAplicativosExternos-ServiceHelper'>Service
Helper</a></li>
</ul></li>
<li><span class='TOCOutline'>4</span> <a
href='#IntegraçãoComAplicativosExternos-ThirdPartyTrademarks'>Third
Party Trademarks</a></li>
</ul>
</div>
</p>
<h1 id="IntegraçãoComAplicativosExternos-ConsideraçõesGerais">Consideraciones
Generales</h1>
<p>La información contenida en este documento tiene el objetivo de
demostrar cómo realizar la integración entre Fluig y aplicaciones
externas. Para una comprensión completa de esta información, algunos
conocimientos se consideran requisitos previos, incluyendo entre
ellos:</p>
<ul>
<li class="O1">Visión general del producto Fluig</li>
<li class="O1">Visión general de la integración de sistemas</li>
<li class="O1">JavaScript</li>
<li class="O1">WebServices</li>
<li class="O1">SOAP</li>
<li class="O1"><p>
<span style="font-size: 10.0pt; line-height: 13.0pt;">Progress®
4GL</span>
</p></li>
<li class="O1">Progress® Open App Server</li>
<li class="O1">Progress® Open Client for <span
style="font-size: 10.0pt; line-height: 13.0pt;">Java</span></li>
<li class="O1">Datasets (Fluig)</li>
<li class="O1">Java™</li>
<li class="O1">Apache Flex®</li>
</ul>
<p>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 Fluig. 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.</p>
<p>Con el fin de facilitar la comprensión de la información que se
presenta y la simulación de los conceptos presentados, los ejemplos
citados en este documento utilizan la funcionalidad de Datasets como
un ejemplo del uso de las capacidades de integración del producto. Sin
embargo, es importante señalar que otros puntos del producto tienen
disponibles las mismas características de integración existentes en
los Datasets, especialmente las personalizaciones de procesos y
formularios.</p>
<h1 id="IntegraçãoComAplicativosExternos-VisãoGeral">Visión
General</h1>
<p>Aunque empíricos, todas las empresas tienen procesos de negocio
que permiten que la empresa cumpla con su objetivo, ya sea la
prestación de un servicio, la producción de bienes materiales o el
comercio de mercancías. Una empresa posee una infinidad de procesos,
así cada persona en la organización participa obligatoriamente en al
menos uno de estos procesos, y todos ellos comparten información entre
sí en algún momento. Los procesos pueden ser formales (como la
contratación de un profesional) o informales (como un incentivo para
la innovación), críticos (facturación) o satélites (control de envío
de tarjetas de cumpleaños).</p>
<p>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.</p>
<p>Fluig 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).</p>
<p>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 Fluig está diseñada para permitir la
integración a estos sistemas, permitiendo que los procesos modelados
tengan mayor valor agregado.</p>
<p>Fluig 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.</p>
<p> </p>
<p>
<img class="confluence-embedded-image image-center"
src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-20%2015%3A33%3A42.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-20%2015%3A33%3A42.png?version=1&modificationDate=1377284793000&api=v2">
</p>
<p> </p>
<p>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 Fluig 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.</p>
<p>La otra forma de integración es a través de llamadas a Progress®
Open AppServer, que se recomienda para usuarios que necesitan integrar
Fluig con aplicaciones desarrolladas en esta plataforma.</p>
<h1 id="IntegraçãoComAplicativosExternos-WebServices">WebServices</h1>
<p>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.</p>
<h3 id="IntegraçãoComAplicativosExternos-AcessandoosWebServicesdoFluig">Acceso
a WebServices de Fluig</h3>
<p>
Fluig 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. <span
style="font-size: 10.0pt; line-height: 13.0pt;">Para tener la
lista de los WebServices disponibles, ingrese a: </span>
</p>
<div class="preformatted panel" style="border-width: 1px;">
<div class="preformattedContent panelContent">
<pre>http://<host>:<porta>/webdesk/services </pre>
</div>
</div>
<p>Cada link que se muestra dirige el navegador a la URL de WSDL
del servicio. WSDL (Web Servicio Description Language) tiene la
descripción del servicio en formato XML y lo utilizan las herramientas
de desarrollo para crear componentes que representarán este servicio.</p>
<div class="aui-message warning shadowed information-macro">
<span class="aui-icon icon-warning">Icon</span>
<div class="message-content">
<p>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:</p>
<ul>
<li>C#: <strong>dateTime</strong></li>
<li>Java™: <strong>XMLGregorianCalendar</strong></li>
<li>Progress®: <strong>DATETIME-TZ</strong></li>
</ul>
</div>
</div>
<p> </p>
<h5 id="IntegraçãoComAplicativosExternos-ViaApacheFlex®">Vía
Apache Flex®</h5>
<p>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.</p>
<p>Utilice el paso a paso para visualizar el proceso de creación de
los stubs para un servicio ofrecido por Fluig:</p>
<a name="composition-deck-stubFlex"></a>
<div id="stubFlex" class="deck" history="false" loopcards="false"
effecttype="" effectduration="1.0" nextafter="0.0">
<ul class="tab-navigation"></ul>
<!-- // .tab-navigation -->
<div class="deck-cards panel" style="">
<div id="1" class="deck-card active-pane" style="" cssclass=""
accesskey="" label="1&ordm; Passo" title="" nextafter="0"
effecttype="fade" effectduration="0.5">
<p>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.</p>
<p>
<img class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-21%2010%3A30%3A3.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-21%2010%3A30%3A3.png?version=1&modificationDate=1377284793000&api=v2">
</p>
</div>
<div id="2" class="deck-card " style="" cssclass="" accesskey=""
label="2&ordm; Passo" title="" nextafter="0" effecttype="fade"
effectduration="0.5">
<p>En la primera ventana, se solicita la carpeta dentro del
proyecto actual donde deben generarse los stubs.</p>
<p>
<img class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-21%2010%3A34%3A29.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-21%2010%3A34%3A29.png?version=1&modificationDate=1377284793000&api=v2">
</p>
</div>
<div id="3" class="deck-card " style="" cssclass="" accesskey=""
label="3&ordm; Passo" title="" nextafter="0" effecttype="fade"
effectduration="0.5">
<p>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.</p>
<p>
<img class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-21%2010%3A36%3A18.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-21%2010%3A36%3A18.png?version=1&modificationDate=1377284793000&api=v2">
</p>
</div>
<div id="4" class="deck-card " style="" cssclass="" accesskey=""
label="4&ordm; Passo" title="" nextafter="0" effecttype="fade"
effectduration="0.5">
<p>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™).</p>
<p>
<img class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-21%2010%3A39%3A19.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-21%2010%3A39%3A19.png?version=1&modificationDate=1377284793000&api=v2">
</p>
</div>
<div id="5" class="deck-card " style="" cssclass="" accesskey=""
label="Resultado" title="" nextafter="0" effecttype="fade"
effectduration="0.5">
<p>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:</p>
<p>
<img class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-21%2010%3A57%3A40.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-21%2010%3A57%3A40.png?version=1&modificationDate=1377284793000&api=v2">
</p>
</div>
</div>
</div>
<!-- // .deck -->
<div class="aui-message hint shadowed information-macro">
<span class="aui-icon icon-hint">Icon</span>
<div class="message-content">
<p>
Para el ejemplo que se presenta a continuación, se utilizó IDE
Adobe® Flex™ Builder™ 3.0 con Flex® SDK 3.2. Para otras versiones de
IDE y/o SDK, el proceso de creación de los stubs puede sufrir
algunas variaciones.<span
style="font-size: 10.0pt; line-height: 13.0pt;"> </span>
</p>
</div>
</div>
<p>El siguiente fragmento de código presenta un ejemplo de
invocación de WebService de acceso a los Datasets de Fluig:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;">
<b>ECMDatasetServiceClient.mxml</b>
</div>
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: actionscript3; gutter: true"
style="font-size: 12px;"><?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></pre>
</div>
</div>
<div class="aui-message warning shadowed information-macro">
<span class="aui-icon icon-warning">Icon</span>
<div class="message-content">
<p>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).</p>
<p>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:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: actionscript3; gutter: true"
style="font-size: 12px;">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
}</pre>
</div>
</div>
<p>Otros servicios que no trabajan con arrays multidimensionales
no exigen cambios en el código generado.</p>
</div>
</div>
<p> </p>
<h5 id="IntegraçãoComAplicativosExternos-ViaJava™">Vía Java™</h5>
<p>Existen muchas implementaciones de uso de WebServices en Java ™
y en este ejemplo vamos a utilizar las bibliotecas disponibles en Java
™ 7.</p>
<p>Del mismo modo que en el ejemplo anterior, en Apache Flex®, el
primer paso consiste en utilizar la dirección WSDL para generar los
stubs en Java ™. El siguiente comando presenta un ejemplo de cómo
generar estos stubs:</p>
<div class="preformatted panel" style="border-width: 1px;">
<div class="preformattedContent panelContent">
<pre>wsimport -d <output_directory> <wsdl_url></pre>
</div>
</div>
<p>A través de este comando se generan los stubs en el directorio
de destino (output_directory), según la descripción del archivo wsdl
(wsdl_url).</p>
<p>
<img class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-20%2016%3A0%3A2.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/image2013-8-20%2016%3A0%3A2.png?version=1&modificationDate=1377284793000&api=v2">
</p>
<p>A partir de los stubs generados, se puede consumir el WebService
como se muestra en el siguiente ejemplo:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;">
<b>ECMDatasetServiceClient.java</b>
</div>
<div class="codeContent panelContent pdl">
<pre class="first-line: 1; theme: Eclipse; brush: java; gutter: true"
style="font-size: 12px;">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();
}
}
}</pre>
</div>
</div>
<div class="aui-message warning shadowed information-macro">
<span class="aui-icon icon-warning">Icon</span>
<div class="message-content">
<p>
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 tipo <strong>fecha </strong>se debe
utilizar la clase <strong>XMLGregorianCalendar:</strong>
</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: java; gutter: true"
style="font-size: 12px;">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);</pre>
</div>
</div>
</div>
</div>
<p> </p>
<h5 id="IntegraçãoComAplicativosExternos-ViaProgress®4GL">Vía
Progress® 4GL</h5>
<p>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.</p>
<p>
Para generar la documentación de un servicio, se debe utilizar la
utilidad <strong>bprowsdldoc</strong> como en el siguiente ejemplo:
</p>
<div class="preformatted panel" style="border-width: 1px;">
<div class="preformattedContent panelContent">
<pre>bprowsdldoc <URL_TO_WSDL></pre>
</div>
</div>
<p>Mediante la ejecución de esta utilidad se generarán algunos
archivos HTML con la información sobre la forma de consumir el
servicio. Esta documentación proporciona información y ejemplos de
cómo llevar a cabo la conexión con el servicio y cómo utilizar los
métodos y datatypes del servicio. </p>
<p>
<span style="font-size: 10.0pt; line-height: 13.0pt;">El
siguiente código presenta un ejemplo de cómo consumir el servicio:</span>
</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;">
<b>wsECMDatasetService.p</b>
</div>
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javafx; gutter: true"
style="font-size: 12px;">/* Parte I - Invocar el 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 ítem 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 ítem.
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 - Realiza el parser de XML y crear un archivo de texto separado por tabulación */
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).
/* Recorre las columnas <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.
/* Recorre los registros <values> */
DO iCount = 1 TO hRoot:NUM-CHILDREN WITH 20 DOWN:
hRoot:GET-CHILD(hValues, iCount).
IF hValues:NAME <> "values" THEN
NEXT.
/* Recorre los 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.</pre>
</div>
</div>
<h3
id="IntegraçãoComAplicativosExternos-AcessandoWebServicesapartirdoFluig">Acceso
a WebServices desde Fluig</h3>
<p>Fluig permite realizar llamadas a WebServices de terceros a
través del registro de Servicios en la visualización de Servicios de
Fluig Studio.</p>
<p>
Para agregar un nuevo WebService, es necesario ingresar a la opción <strong>Incluir
Servicio, </strong>abriendo el asistente Nuevo Servicio, e informar al
servidor de Fluig donde se agregará el servicio, <span
style="font-size: 10.0pt; line-height: 13.0pt;">un código
identificador para él, su descripción, la URL para WSDL y su tipo (en
este caso WebService). </span><span
style="font-size: 10.0pt; line-height: 13.0pt;">En el
siguiente ejemplo, se utilizará un WebService público para consultar
la tabla periódica, cuya dirección de WSDL es </span><span
class="nolink" style="font-size: 10.0pt; line-height: 13.0pt;"><a
href="http://www.webservicex.com/periodictable.asmx?wsdl"
class="external-link" rel="nofollow">http://www.webservicex.com/periodictable.asmx?wsdl</a></span><span
style="font-size: 10.0pt; line-height: 13.0pt;">.</span>
</p>
<p>
<span style="font-size: 10.0pt; line-height: 13.0pt;"><img
class="confluence-embedded-image image-center"
src="http://tdn.totvs.com/download/attachments/73082260/acesso-ws-1.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/acesso-ws-1.png?version=1&modificationDate=1377284793000&api=v2"></span>
</p>
<p>Basado en esta información, Fluig extraerá la información sobre
el WebService informado y finalizará el registro de este
servicio. </p>
<p>
<span style="font-size: 10.0pt; line-height: 13.0pt;">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.</span>
</p>
<p>
<span style="font-size: 10.0pt; line-height: 13.0pt;"><img
class="confluence-embedded-image image-center"
src="http://tdn.totvs.com/download/attachments/73082260/acesso-ws-2.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/acesso-ws-2.png?version=1&modificationDate=1377284793000&api=v2"></span>
</p>
<p>
Los servicios agregados en Fluig 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. <span
style="font-size: 10.0pt; line-height: 13.0pt;">En el
siguiente ejemplo se creará un Dataset que usará este servicio para
traer los datos de la tabla periódica.</span>
</p>
<p>El siguiente código presenta una implementación de ejemplo del
uso de un servicio en la construcción de un Dataset:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;">
<b>periodicTable.js</b>
</div>
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javascript; gutter: true"
style="font-size: 12px;">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;
}</pre>
</div>
</div>
<p>
El primer paso para invocar el servicio es solicitar a Fluig que
cargue el servicio, a partir del método <strong>ServiceManager.getService('PeriodicTable')</strong>.
El valor informado como parámetro debe ser el código utilizado cuando
se registró el servicio.
</p>
<p>
Cuando el servicio se haya cargado, se utiliza el método <strong>getBean()</strong>
para devolver una utilidad para acceder a las clases del servicio, a
través del método <strong>instantiate</strong>. A través de esta
utilidad, se pueden instanciar las clases disponibles, que se
encuentran en el registro del Servicio (como se puede ver en la imagen
anterior).
</p>
<p>Después de que se haya instanciado el objeto utilidad del
servicio, las clases se deben instanciar y los métodos que se deben
invocar depende de cada WebService utilizado y, deben recurrir a su
documentación para obtener más información.</p>
<p>Para el servicio de la tabla periódica es necesario realizar los
siguientes pasos:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javascript; gutter: true"
style="font-size: 12px;">var serviceLocator = serviceHelper.instantiate('net.webservicex.Periodictable');
var service = serviceLocator.getPeriodictableSoap();
var result = service.getAtoms();</pre>
</div>
</div>
<p>Donde:</p>
<ul>
<li><p>
<strong>Paso 1: </strong>Instanciar la clase <strong><span
style="font-size: 10.0pt; line-height: 13.0pt;">net.webservicex.Periodictable</span></strong><span
style="font-size: 10.0pt; line-height: 13.0pt;"> para
tener acceso al localizador del servicio; </span>
</p></li>
<li><span style="font-size: 10.0pt; line-height: 13.0pt;"><strong>Paso
2:</strong> Invocar el método <strong>getPeriodictableSoap</strong> para
instanciar el servicio;</span></li>
<li><span style="font-size: 10.0pt; line-height: 13.0pt;"><strong>Paso
3:</strong> Invocar el método <strong>getAtoms</strong> para tener la lista
de los elementos.</span><br />
<span style="font-size: 10.0pt; line-height: 13.0pt;"><br /></span></li>
</ul>
<p>
En el caso de este servicio, el método <strong>getAtoms</strong>
devuelve una string que contiene un XML con la lista de todos los
elementos, como se muestra en el siguiente ejemplo:
</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: html/xml; gutter: true"
style="font-size: 12px;"><NewDataSet>
<Table>
<ElementName>Actinium</ElementName>
</Table>
<Table>
<ElementName>Aluminium</ElementName>
</Table>
...
</NewDataSet></pre>
</div>
</div>
<p>
Para recorrer el XML y extraer los datos disponibles, se utilizan las
funcionalidades de tratamiento de XML de JavaScript que facilita la
manipulación de datos de este tipo. Para más información sobre esta
funcionalidad, ingrese a : <a
href="http://www.ecma-international.org/publications/standards/Ecma-357.htm"
style="font-size: 10.0pt; line-height: 13.0pt;" class="external-link"
rel="nofollow">http://www.ecma-international.org/publications/standards/Ecma-357.htm</a><span
style="font-size: 10.0pt; line-height: 13.0pt;"> o </span><a
href="http://www.xml.com/pub/a/2007/11/28/introducing-e4x.html"
style="font-size: 10.0pt; line-height: 13.0pt;" class="external-link"
rel="nofollow">http://www.xml.com/pub/a/2007/11/28/introducing-e4x.html</a><span
style="font-size: 10.0pt; line-height: 13.0pt;">.</span>
</p>
<p>El siguiente ejemplo presenta el código utilizado para recorrer
el XML devuelto:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javascript; gutter: true"
style="font-size: 12px;">var NewDataSet = new XML(result);
for each(element in NewDataSet.Table) {
dataset.addRow(new Array(element.ElementName.toString()));
}</pre>
</div>
</div>
<p>Una vez implementado el código del Dataset, es posible
visualizarlo, como muestra la siguiente figura:</p>
<p>
<img class="confluence-embedded-image image-center"
src="http://tdn.totvs.com/download/attachments/73082260/acesso-ws-3.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/acesso-ws-3.png?version=1&modificationDate=1377284793000&api=v2">
</p>
<p> </p>
<h5
id="IntegraçãoComAplicativosExternos-WebServicescomAutenticaçãoBásica">WebServices
con Autenticación Básica</h5>
<p>
Para consumir WebServices que utilizan la autenticación básica (<em>WSS</em>
o <em>WS-Security</em>), es necesario utilizar el método <strong>getBasicAuthenticatedClient </strong>localizado
en provider del servicio (el mismo que se obtiene vía <strong>ServiceManager). </strong>Este
método proporciona un client autenticado.
</p>
<p> </p>
<p>Los parámetros que se deben informar en el método siguen el
siguiente orden:</p>
<ol>
<li>Instancia del servicio</li>
<li>Nombre de la clase del servicio</li>
<li>Usuario para la autenticación</li>
<li>Contraseña para la autenticación</li>
</ol>
<p>
Utilizando el ejemplo del servicio <strong>PeriodicTable </strong>presentado
anteriormente, el código de la llamada tendría los siguientes cambios:
</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javascript; gutter: true"
style="font-size: 12px;">var serviceLocator = serviceHelper.instantiate('net.webservicex.Periodictable');
var service = serviceLocator.getPeriodictableSoap();
var authenticatedService = serviceHelper.getBasicAuthenticatedClient(service, "net.webservicex.PeriodictableSoap", 'usuario', 'contraseña');
var result = authenticatedService.getAtoms();</pre>
</div>
</div>
<h5
id="IntegraçãoComAplicativosExternos-WebServicecomclientcustomizado">
<span
style="color: rgb(0, 0, 0); font-size: 1.8em; font-weight: bold; line-height: normal;"><br /></span>WebService
con client personalizado
</h5>
<div class="aui-message warning shadowed information-macro">
<p class="title">Atención</p>
<span class="aui-icon icon-warning">Icon</span>
<div class="message-content">
<p>Esta técnica es válida para Fluig 1.3.7 o superior.</p>
<p>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".</p>
</div>
</div>
<p>
Para personalizar el client que accede al servicio se debe utilizar el
método <strong>getCustomClient</strong>, localizado en provider
del servicio (el mesmo que se obtiene vía <strong>ServiceManager</strong>).
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 <em
style="line-height: 1.4285715;">snippet</em>:
</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeContent panelContent pdl">
<pre class="theme: Eclipse; brush: js; gutter: false"
style="font-size: 12px;"> 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();</pre>
</div>
</div>
<p> </p>
<p>Los parámetros que se pueden configurar son los siguientes:</p>
<div class="table-wrap">
<table class="confluenceTable">
<tbody>
<tr>
<th class="confluenceTh">Propriedad</th>
<th class="confluenceTh">Función</th>
</tr>
<tr>
<td class="confluenceTd"><pre>basic.authorization</pre></td>
<td class="confluenceTd"><p>
Cuando se define como "true", hace lo mismo que el método <strong>getBasicAuthenticatedClient</strong>,
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<span style="line-height: 1.4285715;">,
las siguientes propiedades con "username" y "password" también
necesitan definirse.</span>
</p></td>
</tr>
<tr>
<td class="confluenceTd"><pre>basic.authorization.username</pre></td>
<td class="confluenceTd">Usuario que se utilizará para
autenticación básica.</td>
</tr>
<tr>
<td class="confluenceTd"><pre>basic.authorization.password</pre></td>
<td class="confluenceTd">Contraseña de usuario utilizado para
autenticación básica.</td>
</tr>
<tr>
<td class="confluenceTd"><pre>disable.chunking</pre></td>
<td class="confluenceTd"><p>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.</p></td>
</tr>
<tr>
<td colspan="1" class="confluenceTd"><pre>log.soap.messages</pre></td>
<td colspan="1" class="confluenceTd">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.</td>
</tr>
</tbody>
</table>
</div>
<p> </p>
<p>
<span
style="color: rgb(0, 0, 0); font-size: 1.8em; font-weight: bold; line-height: normal;">Progress®
Open AppServer</span>
</p>
<p>Así como es posible invocar operaciones en WebServices, Fluig
también permite realizar llamadas a programas en Progress® 4GL (o ABL)
expuestos vía Progress® Open AppServer.</p>
<p>En los siguientes ejemplos, se crearán Datasets que a través de
la capa de servicio, tendrán acceso a las lógicas en 4GL que
realizarán la extracción de datos. Aunque los códigos 4GL, en este
ejemplo, sean muy simples, abarcan los casos más comunes requeridos en
el día a día, ya que la complejidad de la integración se encuentra en
las interfaces (parámetros de entrada y salida) de cada procedure
expuesto y no en su lógica interna.</p>
<p>Tenga en cuenta que los ejemplos que aquí se presentan pretenden
demostrar la dinámica de la integración entre Progress® y Fluig sin
entrar en detalles específicos de las tecnologías involucradas. La
capa de servicios Progress® de Fluig 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®.</p>
<h3 id="IntegraçãoComAplicativosExternos-CasodeUso">Caso de Uso</h3>
<p>
Los ejemplos que se muestran a continuación, tienen como objetivo
crear cuatro Datasets <sup>1</sup> en Fluig:
</p>
<ol>
<li><strong>Tipos de Centro de Costo, </strong>que deben
devolver los tipos de centros de coste existente en la aplicación en
Progress® (en este caso, EMS2).</li>
<li><strong>Naturaleza de los Centros de Costo, </strong>que
debe devolver los tipos posibles de naturaleza, según la aplicación
en Progress® (en este caso, EMS2).</li>
<li><strong>Centros de Costo, </strong>que debe devolver los
registros en la tabla cuenta <sup>2</sup>.</li>
<li><p>
<strong>Usuarios Comunes, </strong>que debe generar una lista
de usuarios comunes entre Fluig y la aplicación en Progress®
(utilizando la tabla usuar_mestre).
</p>
<div class="aui-message warning shadowed information-macro">
<span class="aui-icon icon-warning">Icon</span>
<div class="message-content">
<p>
<span>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.</span>
</p>
<p>
<span>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.</span>
</p>
</div>
</div></li>
</ol>
<p>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:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;">
<b>CostCenterUtils.p</b>
</div>
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javafx; gutter: true"
style="font-size: 12px;">/**************************************************************************
** 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.</pre>
</div>
</div>
<p>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:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;">
<b>verifyUsers.p</b>
</div>
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javafx; gutter: true"
style="font-size: 12px;">/**************************************************************************
** 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.</pre>
</div>
</div>
<p>Los dos códigos presentados tienen diferencias significativas en
la forma en la que se utilizan y en cómo van a ser expuestos por
Progress®. En el primer caso, el programa se carga de manera
persistente y sus procedures se pueden ejecutar de forma
independiente. En el segundo, el programa se ejecuta de manera no
persistente y la lógica principal se encuentra en el main-block. Los
procedures internos, si existen, están destinados a mejorar la
organización del código y no se pueden utilizar de manera aislada.</p>
<h3 id="IntegraçãoComAplicativosExternos-ConfiguraçãodoAppServer">
<a name="_Toc324776230" rel="nofollow"></a>Configuración de AppServer
</h3>
<p>Algunas informaciones importantes en la configuración del
AppServer:</p>
<ol>
<li>AppServer debe cargarse en el modo Stateless;</li>
<li><p>En la configuración del agente, en el campo Propath,
se debe agregar el directorio donde están ubicados los archivos
compilados (.r).</p>
<div class="aui-message warning shadowed information-macro">
<span class="aui-icon icon-warning">Icon</span>
<div class="message-content">
<p>
<strong>Importante:</strong> Cuando se utiliza una ruta relativa
(\\servidor\carpeta), el servicio Windows® de Progress®
(AdminService) se debe iniciar con un usuario de la red que posea
permiso para acceder al directorio informado.
</p>
</div>
</div></li>
</ol>
<h3 id="IntegraçãoComAplicativosExternos-Expondocódigos4GLcomProxyGen">Exponiendo
códigos 4GL con ProxyGen</h3>
<p>
<span style="font-size: 10.0pt; font-family: Arial, sans-serif;">El
primer paso para poder ejecutar rutinas en Progress® 4GL es crear la
biblioteca cliente, que se realiza mediante la aplicación ProxyGen,
que acompaña a la instalación de Progress®, como se muestra a
continuación.</span>
</p>
<p>Utilice el paso a paso para visualizar el proceso de creación
del proxy:</p>
<a name="composition-deck-proxyGen"></a>
<div id="proxyGen" class="deck" history="false" loopcards="false"
effecttype="fade" effectduration="0.5" nextafter="0.0">
<ul class="tab-navigation"></ul>
<!-- // .tab-navigation -->
<div class="deck-cards panel" style="">
<div id="1" class="deck-card active-pane" style="" cssclass=""
accesskey="" label="1&ordm; Passo" title="" nextafter="0"
effecttype="default" effectduration="-1.0">
<p>
<span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;">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).</span>
</p>
<p>
<span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;"><img
class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/proxygen-1.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/proxygen-1.png?version=1&modificationDate=1377284793000&api=v2"><br /></span>
</p>
</div>
<div id="2" class="deck-card " style="" cssclass="" accesskey=""
label="2&ordm; Passo" title="" nextafter="0"
effecttype="default" effectduration="-1.0">
<p>
<span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;">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 <strong>Generate.</strong>
</span>
</p>
<p>
<span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;"><img
class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/proxygen-2.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/proxygen-2.png?version=1&modificationDate=1377284793000&api=v2"><br /></span>
</p>
</div>
<div id="3" class="deck-card " style="" cssclass="" accesskey=""
label="3&ordm; Passo" title="" nextafter="0"
effecttype="default" effectduration="-1.0">
<p>
D<span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;">urante
el </span><span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;">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. </span><span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;">Observe
también el campo AppService, éste debe ser el </span><span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;">nombre
del servicio publicado en AppServer, de lo contrario no se </span><span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;">podrá
conectar al servidor. </span>
</p>
<p>
<span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;"><img
class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/proxygen-3.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/proxygen-3.png?version=1&modificationDate=1377284793000&api=v2"><br /></span>
</p>
</div>
<div id="4" class="deck-card " style="" cssclass="" accesskey=""
label="4&ordm; Passo" title="" nextafter="0"
effecttype="default" effectduration="-1.0">
<p>
<span style="font-size: 10.0pt; font-family: Arial, sans-serif;">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 <strong>OK.</strong>
</span>
</p>
<p>
<span style="font-size: 10.0pt; font-family: Arial, sans-serif;"> </span><img
class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/proxygen-4.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/proxygen-4.png?version=1&modificationDate=1377284793000&api=v2">
</p>
</div>
<div id="5" class="deck-card " style="" cssclass="" accesskey=""
label="&Uacute;ltimo Passo" title="" nextafter="0"
effecttype="default" effectduration="-1.0">
<p style="text-align: left;">
<span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;">Una
vez </span><span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;">creadas
las clases, es necesario empaquetarlas en un archivo .JAR. Esto se
puede </span><span
style="font-size: 10.0pt; line-height: 13.0pt; text-align: justify;">realizar
por línea de comando, utilizando el siguiente comando:</span>
</p>
<div class="preformatted panel" style="border-width: 1px;">
<div class="preformattedContent panelContent">
<pre>jar -cvf <jar_file_name> <directorio></pre>
</div>
</div>
<p class="MsoNormal"
style="margin-bottom: 6.0pt; text-align: justify;">
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<strong> con</strong> debe incluirse y
estar en la raíz del archivo JAR. <span
style="font-size: 10.0pt; line-height: 13.0pt;">Por ser
compatible con el formato ZIP, otra opción es generar un </span><span
style="font-size: 10.0pt; line-height: 13.0pt;">archivo con
las clases generadas (respetando los directorios) y renombrarlo
con </span><span style="font-size: 10.0pt; line-height: 13.0pt;">la
extensión .JAR.</span>
</p>
</div>
</div>
</div>
<!-- // .deck -->
<div class="aui-message hint shadowed information-macro">
<span class="aui-icon icon-hint">Icon</span>
<div class="message-content">
<p>
<span style="font-size: 10.0pt; font-family: Arial, sans-serif;">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</span>
</p>
</div>
</div>
<h3 id="IntegraçãoComAplicativosExternos-ConfiguraçãodoServiçonoFluig">Configuración
del Servicio en Fluig</h3>
<p>
El registro de un servicio se realiza a través de Fluig Studio,
en la view Visualización de Servicios, en la opción  <strong>Incluir
Servicio. </strong>La siguiente pantalla muestra el asistente del nuevo
servicio y los campos utilizados para el registro del servicio
Progress®:
</p>
<p>
<img class="confluence-embedded-image image-center"
src="http://tdn.totvs.com/download/attachments/73082260/servico-process-1.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/servico-process-1.png?version=1&modificationDate=1377284793000&api=v2">
</p>
<p>Donde:</p>
<ul>
<li><strong><strong>Servidor</strong>: </strong>Servidor de
Fluig donde se agregará el servicio;</li>
<li><strong>Código: </strong>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;</li>
<li><strong>Descripción: </strong>Texto que describe el
servicio de datos;</li>
<li><strong style="font-size: 10.0pt; line-height: 13.0pt;">URL: </strong><span
style="font-size: 10.0pt; line-height: 13.0pt;">Identifica la
URL de acceso al servicio AppServer, como por ejemplo
AppServer://<servidor>/<nome_serviço>;</span><br />
<span style="font-size: 10.0pt; line-height: 13.0pt;">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>. </span><br />
<span style="font-size: 10.0pt; line-height: 13.0pt;">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.</span></li>
<li><strong style="font-size: 10.0pt; line-height: 13.0pt;">Tipo: </strong><span
style="font-size: 10.0pt; line-height: 13.0pt;">Identifica el
tipo de servicio (Progress o WebService). Se debe seleccionar
Progress;</span></li>
<li><strong style="font-size: 10.0pt; line-height: 13.0pt;">Objeto
Remoto: </strong><span style="font-size: 10.0pt; line-height: 13.0pt;">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. </span><br />
<span style="font-size: 10.0pt; line-height: 13.0pt;">Ejemplo:
En las pantallas que presentan ProxyGen, se utilizó el paquete </span><strong
style="font-size: 10.0pt; line-height: 13.0pt;">com.fluig.samples.ems</strong><span
style="font-size: 10.0pt; line-height: 13.0pt;">, y el nombre
que se dio al proyecto en ProxyGen fue </span><strong
style="font-size: 10.0pt; line-height: 13.0pt;">EMSProxies</strong><span
style="font-size: 10.0pt; line-height: 13.0pt;">. En este
caso, la clase del objeto remoto será </span><strong
style="font-size: 10.0pt; line-height: 13.0pt;">com.fluig.samples.ems.EMSProxies</strong><span
style="font-size: 10.0pt; line-height: 13.0pt;">;</span></li>
<li><strong style="font-size: 10.0pt; line-height: 13.0pt;">Usuario: </strong><span
style="font-size: 10.0pt; line-height: 13.0pt;">Usuario
utilizado para conectarse al servicio, como se define en la
configuración de AppServer;</span></li>
<li><strong style="font-size: 10.0pt; line-height: 13.0pt;">Contraseña: </strong><span
style="font-size: 10.0pt; line-height: 13.0pt;">Contraseña
utilizada para conectarse al servicio, como se define en la
configuración de AppServer;</span></li>
<li><strong style="font-size: 10.0pt; line-height: 13.0pt;">Parámetros
Extras: </strong><span style="font-size: 10.0pt; line-height: 13.0pt;">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®;</span></li>
<li><strong style="font-size: 10.0pt; line-height: 13.0pt;">Directorio
del archivo de Proxy: </strong><span
style="font-size: 10.0pt; line-height: 13.0pt;">Archivo .JAR
que contiene las clases generadas por ProxyGen. Se debe utilizar el
botón Seleccionar Archivo para localizarlo.</span><br />
<span style="font-size: 10.0pt; line-height: 13.0pt;"><br /></span></li>
</ul>
<p>Cuando el servicio se haya agregado, se pueden ver las clases
disponibles y los métodos existentes en cada una de ellas. Esta
información es importante para guiar el desarrollo de los códigos de
personalización que harán uso de este servicio. Para ver las clases y
los métodos del servicio, utilice la opción Consulta Servicio en
Visualización de Servicios, como muestra la siguiente pantalla:</p>
<p>
<strong><img class="confluence-embedded-image image-center"
src="http://tdn.totvs.com/download/attachments/73082260/servico-process-2.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/servico-process-2.png?version=1&modificationDate=1377284793000&api=v2"><br /></strong>
</p>
<h3
id="IntegraçãoComAplicativosExternos-VisãoGeraldosObjetosEnvolvidos">Visión
general de los objetos involucrados</h3>
<p>El acceso a los procedures expuestos en AppServer implica cuatro
elementos que interactúan entre sí de acuerdo al siguiente diagrama:</p>
<p> </p>
<p>
<img class="confluence-embedded-image image-center"
src="attachments/73082260/73406794.png"
data-image-src="attachments/73082260/73406794.png">
</p>
<p> </p>
<p>Donde:</p>
<ul>
<li><strong>Script Code</strong>: 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.</li>
<li><strong>Service Provider:</strong> 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.</li>
<li><strong>Service Helper:</strong> 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
<a href="73082260.html">aquí</a>.</li>
<li><strong>ProxyGen Clases:</strong> 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.</li>
</ul>
<div> </div>
<h5
id="IntegraçãoComAplicativosExternos-ProceduresPersistenteseNão-Persistentes">Procedures
Persistentes y No Persistentes</h5>
<p>Cuando se agrega un procedure al proyecto de ProxyGen, éste debe
configurarse en dos listas: Procedures Persistentes o No Persistentes
Esta decisión implica directamente en la forma en la que se accede a
estos objetos por la biblioteca generada y, por consiguiente, en la
forma en la que el desarrollador accederá a ella en los códigos
JavaScript.</p>
<p>Los procedures expuestos de forma no persistente dan origen a
métodos en la clase configurada como Objeto Remoto (o Proxy) en el
Servicio, y su ejecución se lleva a cabo llamando al método
correspondiente, por ejemplo:</p>
<div class="preformatted panel" style="border-width: 1px;">
<div class="preformattedContent panelContent">
<pre>serviceHelper.getProxy().verifyUsers(inputTT, holder).</pre>
</div>
</div>
<p>
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 <strong>createManagedObject</strong>. La
llamada a través del método <strong>createManagedObject</strong>
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 <strong>_release()</strong>), bajo
pena de bloquear un nuevo agente de AppServer en cada invocación del
método.
</p>
<p> </p>
<h5 id="IntegraçãoComAplicativosExternos-ParâmetrosdeEntradaeSaída">Parámetros
de Entrada y Salida</h5>
<p>Otro punto importante en la invocación de las rutinas en 4GL es
observar cuales son tipos de parámetros de entrada y salida de cada
procedure o programa. Dependiendo del tipo de dato (CHARACTER,
INTEGER, TEMP-TABLE, etc.), del tipo de parámetro (INPUT,
INPUT-OUTPUT, BUFFER, etc.) y de la versión utilizada de Progress®, la
forma de manipular estos parámetros puede variar.</p>
<p>Para los tipos más simples, como strings, datas o valores
enteros, ProxyGen utiliza un mapeo directo para los tipos o clases
estándares del lenguaje Java™. Para tipos complejos, como temp-tables
y buffers, ProxyGen utiliza clases que forman parte de la biblioteca
de runtime de estos servicios.</p>
<p>
Cuando los parámetros son de entrada y salida (INPUT-OUTPUT) o de
salida (OUTPUT), los tipos primitivos deben sustituirse por clases <em>Holders</em>,
que contendrán el valor devuelto después de la ejecución del método.
Los ejemplos más comunes son StringHolder o ResultSetHolder.
</p>
<p>Los tipos de datos utilizados por cada método se pueden
consultar a través de la Visualización de Servicios en
Fluig Studio. Tenga en cuenta que dependiendo de la versión de
Progress® puede existir variación en los tipos de parámetros
utilizados y en la manera de utilizarlos. En caso de duda, consulte la
documentación suministrada por Progress®.</p>
<h3 id="IntegraçãoComAplicativosExternos-CriaçãodosDatasets">Creación
de los Datasets</h3>
<p>Cuando el servicio Progress® se haya agregado en Fluig, es
posible utilizarlo en los puntos donde el producto permite la
personalización utilizando JavaScript, como en los scripts para
eventos globales, eventos de procesos, eventos de definición de
formulario o Datasets.</p>
<p>La forma de invocar las rutinas expuestas por el servicio es
siempre la misma, independientemente de qué punto se está llamando.
Sin embargo, para facilitar la comprensión del uso de servicios
Progress® en Fluig y facilitar la reproducción de los ejemplos
presentados en el entorno del lector, todos los siguientes ejemplos
utilizarán Datasets como meta.</p>
<p>
Como ya se ha visto anteriormente, los Datasets que se presentarán
aquí son <strong>Tipos de Centro de Costo</strong>, <strong>Naturaleza
de los Centros de Costo</strong>, <strong>Centros de Costo</strong> y <strong>Usuarios
en Común</strong>.
</p>
<p> </p>
<h5 id="IntegraçãoComAplicativosExternos-TiposdeCentrodeCusto">Tipos
de Centro de Costo</h5>
<p>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:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;">
<b>dsTipoCentroCosto.js</b>
</div>
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javascript; gutter: true"
style="font-size: 12px;">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;
}</pre>
</div>
</div>
<p>Donde:</p>
<ul>
<li><strong>Paso 1: </strong>Crea el dataset y agrega los
campos del mismo;</li>
<li><strong>Paso 2:</strong> La invocación del servicio
registrada en Fluig se realiza por el método <strong>ServiceManager.getService</strong>,
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;</li>
<li><strong>Paso 3:</strong> Utiliza el método <strong>getBean</strong> 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;</li>
<li><strong>Paso 4:</strong> Realiza la carga del objeto <strong>CostCenterUtils</strong>
de manera gestionada, a través del método <strong>createManagedObject</strong>
de la utilidad creada anteriormente;</li>
<li><strong>Paso 5:</strong> Invoca el método deseado, en este
caso <strong>readCostTypes</strong>, pasando un StringHolder que
recibirá el valor de salida;</li>
<li><strong>Paso 6:</strong> 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;</li>
<li><strong>Paso 7:</strong> Recorre el array creado, agregando
una línea en el Dataset para cada ítem del array. </li>
</ul>
<p>La siguiente pantalla presenta la visualización de los datos del
Dataset creado:</p>
<p>
<img class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/diagrama%20objetos%20envolvidos.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/diagrama%20objetos%20envolvidos.png?version=1&modificationDate=1377284793000&api=v2">
</p>
<p> </p>
<h5 id="IntegraçãoComAplicativosExternos-NaturezadosCentrosdeCusto">Naturaleza
de los Centros de Costo</h5>
<p>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:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;">
<b>dsNaturalezaCentroCosto.js</b>
</div>
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javascript; gutter: true"
style="font-size: 12px;">function createDataset(fields, constraints, sortFields) {
var dataset = DatasetBuilder.newDataset();
dataset.addColumn("id");
dataset.addColumn("descripcion");
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;
}</pre>
</div>
</div>
<p>Después del registro del Dataset, se puede ver su contenido:</p>
<p>
<img class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/ems-dt2.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/ems-dt2.png?version=1&modificationDate=1377284793000&api=v2">
</p>
<p> </p>
<h5 id="IntegraçãoComAplicativosExternos-CentrosdeCusto">Centros
de Costo</h5>
<p>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.</p>
<p>Dependiendo de la versión de Progress®, los objetos utilizados
pueden variar. A continuación, se presentan ejemplos de la
codificación para Progress® 9 y OpenEdge® 10, respectivamente. En
ambos casos, el resultado presentado por Dataset será el mismo.</p>
<h6 id="IntegraçãoComAplicativosExternos-CodificaçãoProgress®9">Codificación
Progress® 9</h6>
<p>Las temp-table en Progress® 9 son manejadas por los objetos que
implementan la interfaz java.sql.ResultSet:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;">
<b>dsCentroCostoP9.js</b>
</div>
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javascript; gutter: true"
style="font-size: 12px;">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;
}</pre>
</div>
</div>
<h6 id="IntegraçãoComAplicativosExternos-CodificaçãoOpenEdge®10">Codificación
OpenEdge® 10</h6>
<p>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:</p>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;">
<b>dsCentroCostoOE10.js</b>
</div>
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javascript; gutter: true"
style="font-size: 12px;">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;
}</pre>
</div>
</div>
<p>Visualización del Dataset:</p>
<p>
<img class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/ems-dt3.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/ems-dt3.png?version=1&modificationDate=1377284793000&api=v2">
</p>
<p> </p>
<h5 id="IntegraçãoComAplicativosExternos-UsuáriosemComum">Usuarios
en Común</h5>
<p>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.</p>
<p>La segunda diferencia es que el código 4GL se implementa en un
programa no persistente, lo que cambia la forma como la lógica se
invoca desde el código JavaScript.</p>
<p>La tercera diferencia que se puede observar en este caso es que
es posible transformar un Dataset en los tipos de datos requeridos por
Progress® (ResultSet o ProDataGraph).</p>
<h6 id="IntegraçãoComAplicativosExternos-CodificaçãoProgress®9.1">Codificación
Progress® 9</h6>
<div class="code panel pdl" style="border-width: 1px;">
<div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;">
<b>dsUsuariosComunesP9.js</b>
</div>
<div class="codeContent panelContent pdl">
<pre
class="first-line: 1; theme: Eclipse; brush: javascript; gutter: true"
style="font-size: 12px;">function createDataset(fields, constraints, sortFields) {
//Crea el nuevo Dataset
|
Índice
Índice | ||||||
---|---|---|---|---|---|---|
|
Considerações Gerais
As informações contidas neste documento têm por objetivo demonstrar como realizar a integração entre o Fluig e aplicativos externos. Para que se tenha uma compreensão completa destas informações, alguns conhecimentos são considerados pré-requisitos, entre eles:
- Visão geral sobre o produto Fluig
- Visão geral sobre integração de sistemas
- JavaScript
- WebServices
- SOAP
Progress® 4GL
- Progress® Open App Server
- Progress® Open Client for Java
- Datasets (Fluig)
- Java™
- Apache Flex®
Em várias partes deste documento serão apresentados trechos de códigos em diferentes linguagens de programação, com o intuito de demonstrar o uso das capacidades de integração do Fluig. Entretanto, este documento não tem por objetivo capacitar o leitor no uso destas tecnologias além do propósito acima descrito, sendo responsabilidade do leitor buscar informações aprofundadas sobre estas linguagens.
Com o intuito de facilitar o entendimento das informações apresentadas e a simulação dos conceitos apresentados, os exemplos citados neste documento utilizam a funcionalidade de Datasets como exemplo de uso das capacidades de integração do produto. Entretanto, é importante ressaltar que outros pontos do produto possuem disponíveis as mesmas características de integração existentes nos Datasets, em especial as customizações de processos e formulários.
Visão Geral
Ainda que empíricos, toda empresa possui processos de negócios que permitem à empresa cumprir o seu objetivo, seja ele a prestação de um serviço, a produção de bens materiais ou o comércio de mercadorias. Uma empresa possui uma infinidade de processos, sendo que cada pessoa na organização obrigatoriamente participa em pelo menos um destes processos, e todos eles trocam informações entre si em algum momento. Os processos podem ser formais (como a contratação de um profissional) ou informais (como um incentivo à inovação), críticos (faturamento) ou satélites (controle de envio de cartões de aniversários).
Com o advento das tecnologias de Sistema da Informação, vários sistemas passaram a dar apoio a estes processos de negócio, especialmente àqueles considerados mais críticos para a operação da empresa. O melhor exemplo disto é a adoção dos sistemas de ERP que dão suporte aos processos de várias áreas da empresa.
O Fluig tem como objetivo ser uma plataforma agnóstica de gestão de processos, documentos e identidades através de uma interface de comunicação colaborativa. Isto pode ser percebido em maior ou menor grau em cada uma das suas funcionalidades, desde as mais simples (como colaboração) até as mais complexas (como DM e BPM).
Entretanto, parte destes processos têm alta dependência dos sistemas de informação já existentes na empresa e, por isso, a arquitetura do Fluig é planejada para permitir integrar-se a estes sistemas, permitindo que os processos modelados tenham maior valor agregado.
O Fluig permite tanto o acesso pelo produto aos sistemas externos (para consultar ou alimentar informações), bem como habilita que outros sistemas venham a conectar-se para a consulta de informações ou para execução de operações transacionais.
O principal canal de integração do produto é através de WebServices, que vêm se tornando o padrão mais comum de integração com qualquer aplicativo. Através deles, é possível ter acesso às funcionalidades do Fluig e dar acesso pelo produto à aplicativos externos. Este documento dedica uma seção específica sobre integração via WebServices.
A outra forma de integração é via chamadas ao Progress® Open AppServer e é indicada para usuários que precisem integrar o Fluig com aplicativos desenvolvidos nesta plataforma.
WebServices
A integração via WebServices utiliza o protocolo SOAP e, por ser um padrão aberto, permite que sistemas desenvolvidos em plataformas totalmente diferentes como Java™, Microsoft® .Net, C, C++, PHP, Ruby, Pearl, Python, entre outras, possam trocar informações entre si de forma transparente.
Acessando os WebServices do Fluig
O Fluig disponibiliza um conjunto de WebServices que permitem o acesso às informações do produto ou a execução de tarefas, como iniciar solicitações de processos por exemplo. Para ter a lista dos WebServices disponíveis, acesse o endereço:
Sem Formato |
---|
http://<host>:<porta>/webdesk/services |
Cada link apresentado direciona o navegador para a URL do WSDL do serviço. O WSDL (Web Service Description Language) possui a descrição do serviço no formato XML e é utilizado pelas ferramentas de desenvolvimento para a criação dos componentes que representarão este serviço.
Nota |
---|
Atente para cada tipo do atributo que é esperado, por exemplo, o atributo expirationDate do objeto DocumentDto[] é uma data, porém cada linguagem interpreta de maneira diferente, veja alguns exemplos abaixo:
|
Via Apache Flex®
Como a grande maioria das ferramentas de desenvolvimento, o Apache Flex® permite criar stubs para o acesso a web services. Estes stubs encapsulam todas as operações de empacotamento e desempacotamento das informações do padrão XML para os tipos nativos da plataforma.
Utilize o passo-a-passo para visualizar o processo de criação dos stubs para um serviço disponibilizado pelo Fluig:
Deck of Cards | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Informações |
---|
Para o exemplo apresentado abaixo, foi utilizado a IDE Adobe® Flex™ Builder™ 3.0 com Flex® SDK 3.2. Para outras versões da IDE e/ou SDK, o processo para criação dos stubs pode sofrer algumas variações. |
O trecho de código abaixo apresenta um exemplo de invocação do WebService de acesso aos Datasets do Fluig:
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
<?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;
//Cria uma instância do Stub de acesso ao serviço
private var ds:ECMDatasetServiceService = new ECMDatasetServiceService();
public function start() : void {
//Cria tipos auxiliares, que serão utilizados na chamado do serviço
var fields:StringArray = new StringArray();
var constraints:SearchConstraintDtoArray = new SearchConstraintDtoArray();
var order:StringArray = new StringArray();
//Define as funções para tratamento do retorno
ds.addEventListener(GetDatasetResultEvent.GetDataset_RESULT, resultGetDataset);
ds.addEventListener(FaultEvent.FAULT,faultGetDataset);
//invoca o método getDataset do serviço
ds.getDataset("adm", 1, "adm", constraints, order, fields, "colleague");
}
//Tratamento dos dados retornados do serviço invocado.
public function resultGetDataset(ev:GetDatasetResultEvent) : void {
//Recupera o retorno do serviço, na forma de um DatasetDto
var dataset:DatasetDto = ev.result as DatasetDto;
//Monta uma string com todos os dados do dataset
var line:String = "";
//Cabeçalho com o nome dos campos
var columnsArray:ArrayCollection = new ArrayCollection(dataset.columns);
for (var j:int = 0; j < columnsArray.length; j++) {
line += columnsArray.getItemAt(j) + "\t";
}
//Linha de dados
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";
}
}
//Mostra a string criada em um textarea na tela
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 um bug do Flex® que impede o funcionamento correto de serviços que trabalhem com matrizes multidimensionais de dados, como no exemplo acima, onde é retornado um array (de linhas do Dataset) de array (das colunas de cada registro). Para contornar este problema, é preciso alterar a classe gerada pelo Flex™ Builder™ que irá encapsular o array multidimensional. No exemplo acima, este classe é a DatasetDto, que deverá ser alterada (linha 11) conforme o exemplo abaixo:
Outros serviços que não trabalhem com arrays multidimensionais não exigem alterações no código gerado. |
Via Java™
Existem muitas implementações de uso de WebServices em Java™ e neste exemplo vamos utilizar as bibliotecas disponíveis no Java™ 7.
Da mesma forma como no exemplo anterior, em Apache Flex®, o primeiro passo consiste em utilizar o endereço WSDL para a geração dos stubs em Java™. O comando abaixo apresenta um exemplo de como gerar estes stubs:
Sem Formato |
---|
wsimport -d <output_directory> <wsdl_url> |
Através deste comando são gerados os stubs no diretório de destino (output_directory), conforme a descrição do arquivo wsdl (wsdl_url).
A partir dos stubs gerados, é possível consumir o WebService como no exemplo abaixo:
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
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;
/*
* Classe para invocar serviço DatasetService
*/
public class ECMDatasetServiceClient {
//Instancia DatasetServiceService.
private ECMDatasetServiceService ecmDatasetServiceService = new ECMDatasetServiceService();
private DatasetService service = ecmDatasetServiceService.getDatasetServicePort();
//Inicia execução da classe
public static void main(String[] args) {
ECMDatasetServiceClient client = new ECMDatasetServiceClient();
//Configura acesso ao 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 {
//Cria os parâmetros utilizados na chamada
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 o serviço de dataset
DatasetDto result = service.getDataset(
companyId, username, password, name, fields, constraints, order);
//Apresenta o cabeçalho
for (String columnName : result.getColumns()) {
System.out.print(columnName + "\t");
}
System.out.println();
//Apresenta as linhas do dataset
for (ValuesDto row : result.getValues()) {
for (Object value : row.getValue()) {
System.out.print(value + "\t");
}
System.out.println();
}
}
} |
Nota | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
Ao utilizar os WebServices via Java™, deve-se atentar para o tipo de cada atributo e para o tipo de retorno do WebService. Por exemplo, para valores do tipo data deve ser utilizado a classe XMLGregorianCalendar:
|
Via Progress® 4GL
Assim como nos exemplos anteriores, o primeiro passo para consumir um Webservice em Progress® é utilizar um utilitário que irá ler o endereço WSDL e gerar as informações necessárias para acessá-lo. Diferente do Java™ e Flex®, o Progress® não gera objetos de stub, mas apenas uma documentação sobre como consumir os serviços descritos no arquivo WSDL. Embora em algumas situações seja possível utilizar os tipos nativos do Progress® como parâmetros, dependendo do tipo de dado utilizado é preciso manipular o XML SOAP para extrair ou enviar uma informação.
Para gerar a documentação de um serviço, deve-se utilizar o utilitário bprowsdldoc como no exemplo abaixo:
Sem Formato |
---|
bprowsdldoc <URL_TO_WSDL> |
Com a execução deste utilitário, serão gerados alguns arquivos HTML com as informações sobre como consumir o serviço. Esta documentação apresenta informações e exemplos de como realizar a conexão com o serviçoe e a utilizar as métodos e datatypes do serviço.
O código abaixo apresenta um exemplo de como consumir o serviço:
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
/* 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. |
Acessando WebServices a partir do Fluig
O Fluig permite fazer chamadas a WebServices de terceiros através do cadastro de Serviços na Visualização de Serviços do Fluig Studio.
Para adicionar um novo WebService, é preciso clicar na opção Incluir Serviço, abrindo o assistente Novo Serviço, e informar o servidor do Fluig onde será adicionado o serviço, um código identificador para ele, a sua descrição, a URL para o WSDL e o seu tipo (neste caso WebService). No exemplo abaixo, será utilizado um WebService público para consulta à tabela periódica, cujo endereço do WSDL é http://www.webservicex.com/periodictable.asmx?wsdl.
Com base nestas informações, o Fluig irá extrair as informações sobre o WebService informado e finalizará o cadastro deste serviço.
Uma vez que o serviço esteja cadastrado, é possível visualizar as classes e métodos disponíveis neste serviço e que serão utilizados nos códigos JavaScript que farão uso do mesmo. A tela abaixo apresenta um exemplo de visualização de WebService.
Os serviços adicionados no Fluig podem ser instanciados e utilizados nos pontos onde o produto permite customização utilizando-se JavaScript, como nos scripts para eventos globais, eventos de processos, eventos de definição de formulário ou Datasets. No exemplo a seguir, será criado um Dataset que fará uso deste serviço para trazer os dados da tabela periódica.
O código abaixo apresenta uma implementação de exemplo do uso de um serviço na construção de um Dataset:
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
function createDataset(fields, constraints, sortFields) {
//Cria o dataset
var dataset = DatasetBuilder.newDataset();
dataset.addColumn("elementName");
// Conecta o servico e busca os livros
var periodicService = ServiceManager.getService('PeriodicTable');
var serviceHelper = periodicService.getBean();
var serviceLocator = serviceHelper.instantiate('net.webservicex.Periodictable');
var service = serviceLocator.getPeriodictableSoap();
//Invoca o serviço
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;
} |
O primeiro passo para invocar o serviço é solicitar ao Fluig que carregue o serviço, a partir do método ServiceManager.getService('PeriodicTable'). O valor passado como parâmetro, deve ser o código utilizado quando cadastrado o serviço.
Uma vez que o serviço tenha sido carregado, é utilizado o método getBean() para retornar um utilitário para acesso às classes do serviço, através do método instantiate. Através deste utilitário, é possível instanciar as classes disponíveis e que estão listadas no cadastro do Serviço (conforme imagem vista anteriormente).
Uma vez que se tenha instanciado o objeto utilitário do serviço, as classes que devem ser instanciadas e os métodos que devem ser invocados dependem de cada WebService utilizado, e deve-se recorrer à sua documentação para mais informações.
Para o serviço da tabela periódica é necessário realizar os seguintes passos:
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
var serviceLocator = serviceHelper.instantiate('net.webservicex.Periodictable');
var service = serviceLocator.getPeriodictableSoap();
var result = service.getAtoms(); |
Onde:
...
Passo 1: Instanciar a classe net.webservicex.Periodictable para ter acesso ao localizador do serviço;
...
No caso deste serviço, o método getAtoms retorna uma string contendo um XML com a lista de todos os elementos, conforme o exemplo abaixo:
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
<NewDataSet>
<Table>
<ElementName>Actinium</ElementName>
</Table>
<Table>
<ElementName>Aluminium</ElementName>
</Table>
...
</NewDataSet> |
Para percorrer o XML e extrair o dados disponíveis, são utilizadas as funcionalidades de tratamento de XML do JavaScript que facilita a manipulação de dados deste tipo. Para mais informações sobre esta funcionalidade, acesse: http://www.ecma-international.org/publications/standards/Ecma-357.htm ou http://www.xml.com/pub/a/2007/11/28/introducing-e4x.html.
O exemplo abaixo apresenta o código utilizado para percorrer o XML retornado:
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
var NewDataSet = new XML(result);
for each(element in NewDataSet.Table) {
dataset.addRow(new Array(element.ElementName.toString()));
} |
Uma vez implementado o código do Dataset, é possível visualizá-lo, conforme a figura abaixo:
WebServices com Autenticação Básica
Para consumir WebServices que fazem uso de autenticação básica (WSS ou WS-Security), é necessário utilizar o método getBasicAuthenticatedClient localizado no provider do serviço (o mesmo que é obtido via ServiceManager). Este método disponibiliza um client autenticado.
Os parâmetros que devem ser informados no método seguem a ordem abaixo:
- Instância do serviço
- Nome da classe do serviço
- Usuário para a autenticação
- Senha para a autenticação
Utilizando o exemplo do serviço PeriodicTable apresentado anteriormente, o código da chamada teria as alterações abaixo:
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||
---|---|---|
| ||
Esta técnica é válida para o Fluig 1.3.7 ou superior. Em integrações que utilizem serviços criados com o CXF com sistemas que não suportam o protocolo HTTP/1.1 (Protheus, por exemplo), é necessário utilizar este método configurando o parâmetro "disable.chunking" com o valor "true". |
Para customizar o client que acessa os serviços, é necessário utilizar o método getCustomClient, localizado no provider do serviço (o mesmo que é obtido via ServiceManager). Esta configuração exige a criação de um mapa de parâmetros com seus respectivos valores para passar ao método, conforme snippet abaixo:
Bloco de código | ||||
---|---|---|---|---|
| ||||
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(); |
Os parâmetros que podem ser definidos são os seguintes:
Propriedade | Função |
---|---|
basic.authorization | Quando definido como "true", faz o mesmo que o método getBasicAuthenticatedClient, porém permite aplicar as configurações de autenticação juntamente com as demais customizações abaixo. Para configurar a autenticação, as propriedades com "username" e "password" abaixo também precisam ser definidas. |
basic.authorization.username | Usuário a ser utilizado para autenticação básica. |
basic.authorization.password | Senha do usuário utilizado para autenticação básica. |
disable.chunking | Quando definido como "true", desabilita o envio de requisições grandes em "pedaços" menores. Pode ser útil quando o serviço chamado não suporta este tipo de requisição. |
log.soap.messages | Quando definido como "true", permite que as mensagens SOAP utilizadas nas requisições feitas aos serviços sejam apresentadas no log do servidor, facilitando a depuração em caso de falhas. |
Progress® Open AppServer
Assim como é possível invocar operações em WebServices, o Fluig também permite fazer chamadas a programas em Progress® 4GL (ou ABL) expostos via Progress® Open AppServer.
Nos exemplos a seguir, serão criados Datasets que, via camada de serviço, farão o acesso à lógicas em 4GL que farão a extração dos dados. Embora os códigos 4GL, neste exemplo, sejam muito simples, eles compreendem os casos mais comuns exigidos no dia-a-dia, uma vez que a complexidade da integração se encontra nas interfaces (parâmetros de entrada e saída) de cada procedure exposta, e não na sua lógica interna.
Observe que os exemplos aqui apresentados têm por objetivo demonstrar a dinâmica de integração entre Progress® e o Fluig sem entrar em detalhes específicos das tecnologias envolvidas. A camada de serviço Progress® do Fluig cria uma interface em JavaScript para a biblioteca Java Open AppServer Client, da Progress® e, por isso, para mais informações sobre como integrar aplicativos Java™ e Progress® consulte a documentação fornecida pela Progress®.
Caso de Uso
Os exemplos exibidos a seguir, têm por objetivo a criação de quatro Datasets 1 no Fluig:
- Tipos de Centro de Custo, que deve retornar os tipos de centro de custo existentes no aplicativo em Progress® (neste caso, o EMS2).
- Natureza dos Centros de Custo, que deve retornar os tipos possíveis de natureza, conforme o aplicativo em Progress® (neste caso, o EMS2).
- Centros de Custo, que deve retornar os registros na tabela conta 2.
Usuários Comuns, que deve gerar uma lista de usuários comuns entre o Fluig e o aplicativo em Progress® (utilizando a tabela usuar_mestre).
Nota 1 - Os exemplos utilizam uma base de dados do EMS2 para consulta de centros de custo e usuários. Entretanto, apenas duas tabelas e 6 campos são utilizados no total, o que não deve prejudicar o entendimento da lógica pelo leitor, nem impedir a criação de um esquema equivalente para testes, caso necessário.
2 - O código apresentado para extração dos centros de custo tem fins meramente didáticos e não pode ser considerado para uso em produção. Para ter mais informações sobre como extrair centros de custos do EMS2, consulte a documentação técnica do mesmo.
Para os três primeiros casos, a lógica de extração das informações desejadas será exposta em um programa com várias procedures, uma para cada necessidade aqui apresentada:
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
/**************************************************************************
** Utilitário que disponibiliza procedures para a extração de informações
** sobre centros de custo.
**************************************************************************/
DEFINE TEMP-TABLE ttCC NO-UNDO
FIELD conta LIKE conta.ct-codigo /* CHARACTER */
FIELD natureza LIKE conta.natureza /* INTEGER */
FIELD tipo LIKE conta.tipo /* INTEGER */
FIELD titulo LIKE conta.titulo. /* CHARACTER */
/*-------------------------------------------------------------------
Procedure: readCostCenters
Objetivo: Retorna uma temp-table com a lista de centros de custo.
----------------------------------------------------------------------*/
PROCEDURE readCostCenters:
DEFINE OUTPUT PARAMETER TABLE FOR ttCC.
FOR EACH conta:
CREATE ttCC.
ASSIGN
ttCC.conta = conta.ct-codigo
ttCC.natureza = conta.natureza
ttCC.tipo = conta.tipo
ttCC.titulo = conta.titulo.
END.
END.
/*-------------------------------------------------------------------
Procedure: readCostNatureTypes
Objetivo: Retorna uma string com as naturezas dos centros de custo,
separadas por vírgula.
----------------------------------------------------------------------*/
PROCEDURE readCostNatureTypes:
DEFINE OUTPUT PARAMETER cNatureList AS CHARACTER NO-UNDO.
cNatureList = {adinc/i01ad047.i 03}.
END.
/*-------------------------------------------------------------------
Procedure: readCostTypes
Objetivo: Retorna uma string com os tipos de centro de custo,
separados por vírgula.
----------------------------------------------------------------------*/
PROCEDURE readCostTypes:
DEFINE OUTPUT PARAMETER cTypeList AS CHARACTER NO-UNDO.
cTypeList = {adinc/i02ad047.i 3}.
END. |
No caso da extração de usuários comuns aos dois produtos, será utilizado um programa único, conforme o código abaixo:
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
/**************************************************************************
** Utilitário que recebe um temp-table com uma lista de usuários e retorna
** outra, apenas com os usuários da lista que existam na base de dados.
**************************************************************************/
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. |
Os dois códigos apresentados têm diferenças significativas na forma como são utilizados e na forma como serão expostos pelo Progress®. No primeiro, o programa é carregado de forma persistente e suas procedures podem ser executadas de forma independente. No segundo caso, o programa é executado de forma não-persistente e a lógica principal se encontra no main-block. As procedures internas, caso existam, têm por objetivo melhorar a organização do código e não podem ser utilizadas de forma isolada.
Configuração do AppServer
Algumas informações importantes na configuração do AppServer:
- O AppServer deve ser carregado no modo Stateless;
Na configuração do agente, no campo Propath, deve ser adicionado o diretório onde estão localizados os arquivos compilados (.r).
Nota Importante: Quando utilizado um caminho relativo (\\servidor\pasta), o serviço Windows® do Progress® (AdminService) deve ser iniciado com um usuário de rede que possua permissão de acesso ao diretório informado.
Expondo códigos 4GL com ProxyGen
O primeiro passo para que seja possível executar rotinas em Progress® 4GL é criar a biblioteca cliente, o que é feito com o uso do aplicativo ProxyGen, que acompanha a instalação do Progress®, conforme o exemplo abaixo.
Utilize o passo-a-passo para visualizar o processo de criação do proxy:
...
startHidden | false |
---|---|
effectDuration | 0.5 |
id | proxyGen |
history | false |
effectType | fade |
...
default | true |
---|---|
id | 1 |
label | 1º Passo |
- Na primeira tela do ProxyGen, o principal ponto que deve ser observado é o nome do Projeto (no exemplo, EMSProxies). A informação deste campo será utilizada pelo ProxyGen para nomear a classe de acesso ao serviço, e que será utilizada na configuração do serviço no Fluig. Nesta tela também é preciso configurar o PROPATH corretamente, para que seja possível encontrar os arquivos compilados (.r).
...
id | 2 |
---|---|
label | 2º Passo |
- O segundo passo consiste em inserir quais procedures serão expostas de forma persistente ou não-persistente. A escolha de qual opção utilizar depende da forma como cada objeto exposto foi construído. Após inseridas as procedures, clicar na opção Generate.
...
id | 3 |
---|---|
label | 3º Passo |
- Durante o processo de geração do proxy, na aba General, assinalar a opção Java em Client Proxy e informar o diretório onde o proxy será gerado em Output Dir. Observe também o campo AppService, este deve ser o nome do serviço publicado no AppServer, caso contrário não será possível conectar ao servidor.
...
id | 4 |
---|---|
label | 4º Passo |
- A última informação relevante para a geração do proxy é o nome do pacote (package) onde as classes serão criadas. Esta informação é utilizada durante a configuração do serviço Progress® no Fluig. Para finalizar clicar no botão OK.
...
id | 5 |
---|---|
label | Último Passo |
...
Sem Formato |
---|
jar -cvf <jar_file_name> <diretorio> |
Observe apenas que no arquivo gerado, é preciso que as classes estejam nos diretórios corretos. No exemplo apresentado, o diretório com deve ser incluído e estar no raiz do arquivo JAR. Por ser compatível com o formato ZIP, uma outra opção é gerar um arquivo com as classes geradas (respeitando-se os diretórios) e renomeá-lo para a extensão .JAR.
Informações |
---|
Dependendo da versão do Progress®, as telas podem sofrer alguma variação na quantidade e disposição dos campos. Consulte a documentação em caso de dúvida |
Configuração do Serviço no Fluig
O cadastro de um serviço é realizado através do Fluig Studio, na view Visualização de Serviços, pela opção Incluir Serviço. A tela abaixo apresenta o assistente de novo serviço e os campos utilizados para o cadastro do serviço Progress®:
Onde:
...
Uma vez que o serviço tenha sido adicionado, é possível visualizar as classes disponíveis e os métodos existentes em cada uma delas. Estas informações são importantes para guiar o desenvolvimento dos códigos de customização que farão uso deste serviço. Para visualizar as classes e métodos do serviço, utilize a opção Consulta Serviço na Visualização de Serviços, conforme a tela abaixo:
Visão Geral dos Objetos Envolvidos
O acesso às procedures expostas no AppServer envolve quatro elementos que interagem entre si, conforme o diagrama abaixo:
Onde:
- Script Code: é o código em JavaScript que fará uso das procedures expostas no AppServer. Como mencionado anteriormente, este JavaScript pode ser de qualquer natureza, como a implementação de um Dataset ou a customização de um evento de processo.
- Service Provider: Objeto recuperado através do método ServiceManager.getService e que fornece o acesso às funcionalidades do serviço. Este objeto é responsável por gerenciar a conexão e recursos alocados pelo serviço durante a execução do script.
- Service Helper: Objeto recuperado via método getBean no ServiceProvider e que disponibiliza um conjunto de métodos utilitários que permitem, entre outras coisas, criar objetos específicos do Progress® (StringHolder, ResultSetHolder, etc.), ter acesso ao objeto remoto do ProxyGen e instanciar classes. Para mais informações sobre o Service Helper consultar aqui.
- ProxyGen Classes: Classes geradas pelo ProxyGen e que serão utilizadas pelo desenvolvedor para execução das rotinas em Progress®. A lista das classes disponíveis, bem como os seus métodos, podem ser visualizados na Visualização de Serviços do Fluig Studio.
Procedures Persistentes e Não-Persistentes
Quando uma procedure é adicionada ao projeto do ProxyGen, ela deve ser configurada em duas listas: Procedures Persistentes ou Não-Persistentes. Esta decisão implica diretamente na forma como estes objetos são acessados pela biblioteca gerada e, consequentemente, na forma como o desenvolvedor irá acessá-las nos códigos JavaScript.
As procedures expostas de forma não-persistente dão origem à métodos na classe configurada como Objeto Remoto (ou Proxy) no Serviço, e a sua execução é feita chamando o método correspondente, por exemplo:
Sem Formato |
---|
serviceHelper.getProxy().verifyUsers(inputTT, holder). |
As procedures expostas de forma persistente dão origem à novas classes que podem ser instanciadas via chamadas a métodos no Objeto Remoto (através da Visualização de Serviços no Fluig Studio é possível verificar os métodos disponíveis na classe), ou via o método createManagedObject. A chamada via o método createManagedObject permite que o Fluig tenha controle sobre o ciclo de vida deste objeto, liberando-o automaticamente ao fim do método. Caso o objeto seja instanciado manualmente, o desenvolvedor deve codificar a liberação do objeto (método _release()), sob pena de bloquear um novo agente do AppServer a cada invocação do método.
Parâmetros de Entrada e Saída
Outro ponto importante na invocação das rotinas em 4GL é observar quais os tipos de parâmetros de entrada e saída de cada procedure ou programa. Dependendo do tipo de dado (CHARACTER, INTEGER, TEMP-TABLE, etc.), do tipo de parâmetro (INPUT, INPUT-OUTPUT, BUFFER, etc.) e da versão utilizada do Progress®, a forma de se manipular estes parâmetros pode variar.
Para os tipos mais simples, como strings, datas ou valores inteiros, o ProxyGen utiliza um mapeamento direto para os tipos ou classes padrões da linguagem Java™. Para tipos complexos, como temp-tables e buffers, o ProxyGen utiliza classes que fazem parte da biblioteca de runtime destes serviços.
Quando os parâmetros são de entrada e saída (INPUT-OUTPUT) ou de saída (OUTPUT), os tipos primitivos precisam ser substituídos por classes Holders, que conterão o valor retornado após a execução do método. Os exemplos mais comuns são StringHolder ou ResultSetHolder.
Os tipos de dados utilizados em cada método podem ser consultado através da Visualização de Serviços no Fluig Studio. Observe que dependendo da versão do Progress® pode haver variação nos tipos de parâmetros utilizados e na forma de utilizá-los. Em caso de dúvida, consulte a documentação fornecida pela Progress®.
Criação dos Datasets
Uma vez que o serviço Progress® tenha sido adicionado no Fluig, é possível utilizá-lo nos pontos onde o produto permite customização utilizando-se JavaScript, como nos scripts para eventos globais, eventos de processos, eventos de definição de formulário ou Datasets.
A forma de se invocar as rotinas expostas pelo serviço é sempre a mesma, independente de qual ponto está sendo chamado. Entretanto, para facilitar o entendimento do uso dos serviços Progress® no Fluig e facilitar a reprodução dos exemplos apresentados no ambiente do leitor, todos os exemplos abaixo utilizarão Datasets como alvo.
Conforme visto anteriormente, os Datasets que serão apresentados aqui são Tipos de Centro de Custo, Natureza dos Centros de Custo, Centros de Custo e Usuários em Comum.
Tipos de Centro de Custo
O código abaixo apresenta a implementação do Dataset de Tipos de Centro de Custo. A explicação de cada passo da implementação será apresentada após o código:
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
function createDataset(fields, constraints, sortFields) {
//Passo 1 - Cria o dataset
var dataset = DatasetBuilder.newDataset();
dataset.addColumn("id");
dataset.addColumn("descricao");
//Passo 2 - Invoca o serviço cadastrado no Fluig
var servico = ServiceManager.getService("EMS2");
//Passo 3 - Carrega o objeto utilitário para integração com Progress
var serviceHelper = servico.getBean();
//Passo 4 - Carrega a procedure persistente CostCenterUtils.p
var remoteObj = serviceHelper.createManagedObject("CostCenterUtils");
//Passo 5 - Invoca a procedure que retorna uma string com os tipos de CC
var types = serviceHelper.createStringHolder();
remoteObj.readCostTypes(types);
//Passo 6 - Quebra a string em um array com cada um dos tipos
var typeArray = types.getStringValue().split(",");
//Passo 7 - Adiciona cada tipo retornado
for(var pos = 0; pos < typeArray.length; pos++) {
dataset.addRow(new Array(pos + 1, typeArray[pos]));
}
return dataset;
} |
Onde:
- Passo 1: Cria o dataset e adiciona os campos do mesmo;
- Passo 2: É feita a invocação do serviço cadastrado no Fluig, através do método ServiceManager.getService, e o valor passado como parâmetro deve ser o código do serviço. Note que neste ponto não é preciso informar qualquer parâmetro de conexão ao serviço, uma vez que isto já foi feito no seu cadastro;
- Passo 3: Utiliza o método getBean para retornar um objeto utilitário para serviços Progress®. Este utilitário disponibiliza uma série de métodos que facilitam a interação com o proxy gerado e seus métodos serão apresentados em mais detalhes adiante neste documento;
- Passo 4: Faz a carga do objeto CostCenterUtils de forma gerenciada, através do método createManagedObject do utilitário criado anteriormente;
- Passo 5: Invoca o método desejado, neste caso o readCostTypes, passando um StringHolder que irá receber o valor de saída;
- Passo 6: Transforma a String recebida por parâmetro em um array com as opções. O caractere , (vírgula) é utilizado para determinar os pontos de quebra da string;
- Passo 7: Percorre o array criado, adicionando uma linha no Dataset para cada item do array.
A tela abaixo apresenta a visualizaçào dos dados do Dataset criado:
Natureza dos Centros de Custo
O Dataset de Natureza dos Centros de Custo é muito similar ao Dataset de tipo de centro de custo. Na prática, a única alteração é a procedure que é chamada:
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
function createDataset(fields, constraints, sortFields) { var dataset = DatasetBuilder.newDataset(); dataset.addColumn("idusuario"); dataset.addColumn("nombre"); //Recupera los usuarios de Fluig var campos = new Array("descricao"); "colleaguePK.colleagueId", "colleagueName"); var colleaguesDataset = DatasetFactory.getDataset("colleague", campos, null, null); //Instancia el servicio var servico = ServiceManager.getService("EMS2"); var serviceHelper = servico.getBean(); var remoteObj //Transforma el dataset en un ResultSet (v9) y crea holder de salida var inputTT = serviceHelpercolleaguesDataset.createManagedObjecttoResultSet("CostCenterUtils"); var typesholder = serviceHelper.createStringHoldercreateResultSetHolder(); remoteObj.readCostNatureTypes(types); var typeArray = types.getStringValue().split(","); //Invoca el procedure en el Progress serviceHelper.getProxy().verifyUsers(inputTT, holder); for(var posrs = 0; pos < typeArray.length; pos++holder.getResultSetValue(); while (rs.next()) { dataset.addRow(new Array(pos + 1, typeArray[pos](rs.getObject("cod_usuar"), rs.getObject("nom_usuario"))); } return dataset; } |
Após o cadastro do Dataset, é possível visualizar o seu conteúdo:
Centros de Custo
O Dataset de Centros de Custo possui uma estrutura muito semelhante aos dois Datasets vistos anteriormente. A diferença principal é que, neste caso, a procedure retorna uma temp-table com os centros de custo, o que altera a forma como os dados são manipulados.
Dependendo da versão do Progress®, os objetos utilizados podem variar. A seguir, são apresentados exemplos da codificação para Progress® 9 e OpenEdge® 10, respectivamente. Em ambos os casos, o resultado apresentado pelo Dataset será o mesmo.
Codificação Progress® 9
As temp-table no Progress® 9 são tratadas através de objetos que implementam a interface java.sql.ResultSet:
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
function</pre> </div> </div> <h6 id="IntegraçãoComAplicativosExternos-CodificaçãoOpenEdge®10.1">Codificación OpenEdge® 10</h6> <div class="code panel pdl" style="border-width: 1px;"> <div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"> <b>dsUsuariosComunesOE10.js</b> </div> <div class="codeContent panelContent pdl"> <pre class="first-line: 1; theme: Eclipse; brush: javascript; gutter: true" style="font-size: 12px;">function createDataset(fields, constraints, sortFields) { //CriaCrea ael estrutura donuevo Dataset var dataset = DatasetBuilder.newDataset(); dataset.addColumn("contausuario"); dataset.addColumn("titulonombre"); dataset.addColumn("natureza"); dataset.addColumn("tipo"); //Recupera olos serviçousuarios e carrega o objeto remotode Fluig var servicocampos = ServiceManager.getService("EMS2 new Array("colleaguePK.colleagueId", "colleagueName"); var serviceHelpercolleaguesDataset = servicoDatasetFactory.getBean(); var remoteObj = serviceHelper.createManagedObject("CostCenterUtils"getDataset("colleague", campos, null, null); //LêInstancia asel contas correntesservicio var holderservico = serviceHelperServiceManager.createResultSetHoldergetService("EMS2"); var serviceHelper = remoteObjservico.readCostCentersgetBean(holder);; //Transforma el dataset en //Percorreun os registros, carregando o Dataset com os dadosProDataGraph (v10) y crea holder de salida var rsinputTT = holderserviceHelper.getResultSetValuetoProDataGraph(colleaguesDataset); while (rs.next()) {var holder = serviceHelper.createProDataGraphHolder(); //Invoca el procedure en el varProgress conta = rs.getObject("conta"serviceHelper.getProxy().verifyUsers(inputTT, holder); var naturezattCC = rs.getObjectholder.getProDataGraphValue().getProDataObjects("naturezattOutUsers"); for (var row_index = var tipo = rs.getObject("tipo");0; row_index < ttCC.size(); row_index++) { var titulo row = rsttCC.getObject("titulo"get(row_index); dataset.addRow(new Array(conta, titulo, natureza, tipo(row.get("cod_usuar"), row.get("nom_usuario"))); } return dataset; } |
Codificação OpenEdge® 10
No OpenEdge® 10, as temp-tables retornadas são encapsuladas como objetos da classe ProDataGraph. Esta classe também é utilizada quando se utilizam parâmetros do tipo DATASET:
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
function createDataset(fields, constraints, sortFields) {
//Cria a estrutura do Dataset
var dataset = DatasetBuilder.newDataset();
dataset.addColumn("conta");
dataset.addColumn("titulo");
dataset.addColumn("natureza");
dataset.addColumn("tipo");
//Recupera o serviço e carrega o objeto remoto
var servico = ServiceManager.getService("EMS2");
var serviceHelper = servico.getBean();
var remoteObj = serviceHelper.createManagedObject("CostCenterUtils");
//Lê as contas correntes
var holder = serviceHelper.createProDataGraphHolder();
remoteObj.readCostCenters(holder);
//Percorre os registros, carregando o Dataset com os dados
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("conta"),
row.get("titulo"),
row.get("natureza"),
row.get("tipo")));
}
return dataset;
} |
Visualização do Dataset:
Usuários em Comum
A primeira diferença entre o Dataset de usuários comuns e os exemplos anteriores, é que neste caso é preciso passar uma temp-table como parâmetro para a procedure invocada.
A segunda diferença é que o código 4GL está implementado em um programa não-persistente, o que altera a forma como a lógica é invocada a partir do código JavaScript.
A terceira diferença que pode se observar neste caso é que é possível transformar um Dataset nos tipos de dados requeridos pelo Progress® (ResultSet ou ProDataGraph).
Codificação Progress® 9
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
function createDataset(fields, constraints, sortFields) {
//Cria o novo Dataset
var dataset = DatasetBuilder.newDataset();
dataset.addColumn("usuario");
dataset.addColumn("nome");
//Recupera os usuários do Fluig
var campos = new Array("colleaguePK.colleagueId", "colleagueName");
var colleaguesDataset = DatasetFactory.getDataset("colleague", campos, null, null);
//Instancia o servico
var servico = ServiceManager.getService("EMS2");
var serviceHelper = servico.getBean();
//Transforma o dataset em um ResultSet (v9) e cria holder para saida
var inputTT = colleaguesDataset.toResultSet();
var holder = serviceHelper.createResultSetHolder();
//Invoca a procedure no 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;
} |
Codificação OpenEdge® 10
Bloco de código | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
function createDataset(fields, constraints, sortFields) {
//Cria o novo Dataset
var dataset = DatasetBuilder.newDataset();
dataset.addColumn("usuario");
dataset.addColumn("nome");
//Recupera os usuários do Fluig
var campos = new Array("colleaguePK.colleagueId", "colleagueName");
var colleaguesDataset = DatasetFactory.getDataset("colleague", campos, null, null);
//Instancia o servico
var servico = ServiceManager.getService("EMS2");
var serviceHelper = servico.getBean();
//Transforma o dataset em um ProDataGraph (v10) e cria holder para saida
var inputTT = serviceHelper.toProDataGraph(colleaguesDataset);
var holder = serviceHelper.createProDataGraphHolder();
//Invoca a procedure no 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;
} |
Visualização do Dataset:
...
Service Helper
A tabela abaixo apresenta a lista de métodos existentes na classe utilitária para serviços Progress®:
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
</pre>
</div>
</div>
<p>Visualización del Dataset:</p>
<p>
<img class="confluence-embedded-image"
src="http://tdn.totvs.com/download/attachments/73082260/ems-dt4.png?version=1&modificationDate=1377284793000&api=v2"
data-image-src="http://tdn.totvs.com/download/attachments/73082260/ems-dt4.png?version=1&modificationDate=1377284793000&api=v2">
</p>
<p>
<span class="confluence-anchor-link"
id="IntegraçãoComAplicativosExternos-servicehelper"></span>
</p>
<h3 id="IntegraçãoComAplicativosExternos-ServiceHelper">Service
Helper</h3>
<p>La siguiente tabla presenta la lista de métodos existentes en la
clase utilidad para servicios Progress®:</p>
<div class="table-wrap">
<table class="confluenceTable">
<tbody>
<tr>
<th class="confluenceTh">Devolución</th>
<th class="confluenceTh">Método y Descripción</th>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createBigDecimalHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo
DECIMAL</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createBooleanHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo
LOGICAL</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createByteArrayHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo RAW</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createCOMHandleHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo
COM-HANDLE</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createDateHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo DATE</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createHandleHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo
WIDGET-HANDLE (Handle)</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createIntHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo
INTEGER</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createLongHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo RECID</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createManagedObject</strong></span>(java.lang.String objName)
</code>
<div class="block">
Lee un archivo .p o .r que haya sido expuesto a través de
AppServer de forma persistente. <span
style="color: rgb(53, 56, 51);">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.</span>
</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createMemptrHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo MEMPTR</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createProDataGraph</strong></span>(java.lang.Object metadata)
</code>
<div class="block">Crea un objeto de la clase ProDataGraph</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createProDataGraphHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo
ProDataGraphHolder</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createProDataGraphMetaData</strong></span>(java.lang.String name)
</code>
<div class="block">Crea un objeto de la clase
ProDataGraphMetadata</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createProDataObjectMetaData</strong></span>(java.lang.String tableName,
int numFields, boolean bimageFlag, int numIndexes,
java.lang.String multiIxCols,
java.lang.String XMLNamespace,
java.lang.String XMLPrefix)
</code>
<div class="block">Crea un objeto de la clase
ProDataObjectMetadata.</div>
<div class="block">Crea un objeto para un dataset
(Temp-table).</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createResultSetHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo TABLE</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createRowidHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo ROWID</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>createStringHolder</strong></span>()
</code>
<div class="block">Crea un objeto Holder para el tipo
CHARACTER.</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>getProxy</strong></span>()
</code>
<div class="block">
Devuelve la instancia del objeto de conexión a AppServer, ya
conectado y disponible para su uso. <span
style="color: rgb(53, 56, 51);">El objeto remoto es la
principal clase generada por ProxyGen.</span>
</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>instantiate</strong></span>(java.lang.String className)
</code>
<div class="block">Instancia un objeto de una clase dentro
de la biblioteca de proxy.</div></td>
</tr>
<tr>
<td class="confluenceTd"><code>java.lang.Object</code></td>
<td class="confluenceTd"><code>
<span style="color: rgb(51, 102, 255);"><strong>toProDataGraph</strong></span>(com.datasul.technology.webdesk.dataset.DefaultDataset d)
</code>
<div class="block">Transforma un dataset en un ProDataGraph.</div></td>
</tr>
</tbody>
</table>
</div>
<h1 id="IntegraçãoComAplicativosExternos-ThirdPartyTrademarks">Third
Party Trademarks</h1>
<p>Adobe and Flex Builder are either registered
trademarks or trademarks of Adobe Systems Incorporated in the United
States and/or other countries.</p>
<p>Apache Flex is a registered trademark of The Apache
Software Foundation.</p>
<p>JavaScript is a trademark of Oracle Corporation.</p>
<p>Microsoft and Windows are registered trademarks of Microsoft
Corporation in the United States and/or other countries.</p>
<p>Java is a trademark of Oracle and/or its affiliates. Other names
may be trademarks of their respective owners.</p>
<p>Progress and OpenEdge are trademarks or registered trademarks of
Progress Software Corporation or one of its subsidiaries or affiliates
in the U.S. and other countries.</p>
<p>This product includes software developed by the Visigoth
Software Society.</p>
<p>
<span style="font-size: 10.0pt; line-height: 13.0pt;">Any other
third party trademarks are the property of their respective owners.</span>
</p>
<p>
<span style="font-size: 10.0pt; line-height: 13.0pt;"><br /></span>
</p>
</div>
|