Árvore de páginas

Versões comparadas

Chave

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

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:

...

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).

...

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: 

...

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:

  • C#: dateTime
  • Java™: XMLGregorianCalendar
  • Progress®: DATETIME-TZ
 
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.

...

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:

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

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.

...

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:

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

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

document.setExpirationDate(date);

 

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.

...

Bloco de código
themeEclipse
languagejavafx
titlewsECMDatasetService.p
firstline1
linenumberstrue
collapsetrue
/* 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 Studio.

...

Uma vez implementado o código do Dataset, é possível visualizá-lo, conforme a figura abaixo:

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.

...

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:

...

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:

  1. O AppServer deve ser carregado no modo Stateless;
  2. 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.

...

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 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®:

...

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:

...

  • 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 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 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 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.

...

Os tipos de dados utilizados em cada método podem ser consultado através da Visualização de Serviços no 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.

...

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:

...

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:

...

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
themeEclipse
languagejavascript
titledsCentroCustoP9.js
firstline1
linenumberstrue
collapsetrue
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.createResultSetHolder();
    remoteObj.readCostCenters(holder);
    
    //Percorre os registros, carregando o Dataset com os dados
    var rs = holder.getResultSetValue();
    while (rs.next()) {
        var conta 	 = rs.getObject("conta");
        var natureza = rs.getObject("natureza");
        var tipo 	 = rs.getObject("tipo");
        var titulo   = rs.getObject("titulo");
	
        dataset.addRow(new Array(conta, titulo, natureza, tipo));
    }
    
    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:

...

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 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
themeEclipse
languagejavascript
titledsUsuariosComunsP9.js
firstline1
linenumberstrue
collapsetrue
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
themeEclipse
languagejavascript
titledsUsuariosComunsOE10.js
firstline1
linenumberstrue
collapsetrue
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;
}

...

Âncora
servicehelper
servicehelper

Service Helper 

A tabela abaixo apresenta a lista de métodos existentes na classe utilitária para serviços Progress®:

...