Importante
Caro Cliente,
O TOTVS ECM 3.0 foi fundamentado na tecnologia de interface Flash, do qual a Adobe irá descontinuar seu suporte em 31/12/2020.
Recomendamos que nossos clientes avaliem a utilização do TOTVS Fluig Plataforma, que proporciona uma maior abrangência de recursos e importantes inovações tecnológicas. São inúmeras novidades não só em ECM e BPM que o Fluig entrega aos seus mais de 4 mil clientes, mas também conta com recursos de portais, social e identidade única.
Entre em contato com seu executivo de conta para saber mais detalhes desta oferta.
As informações contidas neste documento têm por objetivo demonstrar como é feita a integração entre o TOTVS | ECM e aplicativos externos. Para que se tenha uma compreensão completa destas informações, alguns conhecimentos são considerados pré-requisitos, entre eles:
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 TOTVS | ECM. 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 fichários.
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 TOTVS | ECM tem como objetivo servir de ferramenta para agilizar e automatizar processos de negócio, especialmente aqueles que não são atendidos plenamente pelos demais sistemas da empresa. 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 GED e Workflow).
Entretanto, parte destes processos têm alta dependência dos sistemas de informação já existentes na empresa e, por isso, a arquitetura do TOTVS | ECM é planejada para permitir integrar-se a estes sistemas, permitindo que os processos modelados tenham maior valor agregado.
O TOTVS | ECM 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.
Figura 1 - Diagrama geral de integração
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 TOTVS | ECM 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 TOTVS | ECM com aplicativos desenvolvidos nesta plataforma.
A integração via WebServices utiliza o protocolo SOAP e , por ser um padrão aberto, permitem que sistemas desenvolvidos em plataformas totalmente diferentes como Java™, Microsoft® .Net, C, C++, PHP, Ruby, Pearl ou Python possam trocar informações entre si de forma transparente.
O TOTVS | ECM disponibiliza um conjunto de WebServices que permitem o acesso às informações do produto ou a execução de tarefas, como iniciar processos workflow por exemplo.
Para ter uma lista dos processos disponíveis, abra o navegador no endereço http://<host>:<porta>/webdesk/services.
Cada link apresentado direciona o browser 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.
Observação: 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 (exemplo de utilização)
Progress®: DATETIME-TZ
Via Adobe® Flex™
Como a grande maioria das ferramentas de desenvolvimento, o Adobe® 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.
A criação dos stubs no Flex™ é feito no menu Data, Opção Import WebService (WSDL), conforme a imagem abaixo.
Figura 2 - Flex Builder - Criando Stubs no Flex
Na primeira janela, é solicitada a pasta dentro do projeto corrente onde devem ser gerados os stubs.
Figura 3 - Flex Builder - Definindo pasta de destino
Na tela a seguir, deve ser informado o endereço do WSDL onde se encontra o serviço. Também é possível definir se ele será acessado da estação cliente ou do servidor LifeCycle Data Services.
Figura 4 - Flex Builder - Informando o WSDL
Na última tela, deve-se informar o package que será utilizado e qual o nome da classe principal (já sugeridos pelo Flex™ Builder™).
Figura 5 - Flex Builder - Informando o WSDL
Uma vez finalizado o processo, o Flex™ Builder™ adicionará ao projeto um conjunto de classes que serão utilizadas pelo programador para invocar os serviços, conforme a figura abaixo:
Figura 6 - Objetos gerados pelo Flex Builder
O trecho de código abaixo apresenta um exemplo de invocação do WebService de acesso aos Datasets do TOTVS | ECM.
<?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.DatasetServiceService; import mx.rpc.events.FaultEvent; import mx.collections.ArrayCollection; /* Cria uma instância do Stub de acesso ao serviço */ private var ds:DatasetServiceService = new DatasetServiceService(); /* Cria tipos auxiliares, que serão utilizados na chamado do serviço */ private var fields:StringArray; private var constraints:SearchConstraintDtoArray; private var order:StringArray; private var search:SearchConstraintDtoArray; private var columnsArray:ArrayCollection; private var valuesArray:ArrayCollection; public function start() : void { /* Busca dataset especifico */ fields = new StringArray(); constraints = new SearchConstraintDtoArray(); order = new StringArray(); /* Define as funções para tratamento do retorno */ ds.addEventListener(GetDatasetResultEvent.GetDataset_RESULT, resultGetDataset); ds.addEventListener(FaultEvent.FAULT,faultGetDataset); ds.getDataset(1, /* Empresa */ "adm", /* Usuario */ "adm", /* Senha */ "estados", /* Nome Dataset */ fields, /* Campos a Retornar */ constraints, /* Condições de busca */ order); /* Campos p/ Ordenação */ } // 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; /* Determina o número de colunas e linhas do dataset */ columnsArray = new ArrayCollection(dataset.columns); valuesArray = new ArrayCollection(dataset.values); /* Monta uma string com todos os dados do dataset */ var line:String = ""; /* Cabeçalho com o nome dos campos */ for (var j:int=0; j<columnsArray.length; j++) { line = line + columnsArray.getItemAt(j) + "\t"; } line = line + "\n"; /* Linha de dados */ for (var j:int=0;j<valuesArray.length;j++) { var row:ValuesDto = valuesArray.getItemAt(j) as ValuesDto; line = line + j + ":"; for (var i:int=0;i<row.length;i++) { line = line + row.getItemAt(i) + "\t"; } line = line + "\n"; } /* 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>
Aviso: Existe um bug do Adobe® 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 conforme o exemplo abaixo:
blic class DatasetDto { /** * Constructor, initializes the type class */ DatasetDto() {} public function DatasetDto() {} [ArrayElementType("String")] public var columns:Array; [ArrayElementType("ValuesDto")] public var values:Array = new Array; /* inicializado o array */ /* public var values:Array ; */ }
Outros serviços que não trabalhem com arrays multidimensionais não exigem alterações no código gerado.
Existem muitas implementações de uso de WebServices em Java™ e neste exemplo vamos utilizar as bibliotecas disponíveis no Java™ 6.
Da mesma forma como no exemplo anterior, em Adobe® 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.
wsimport -d <output_directory >
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).
Figura 7 - Classes geradas pelo utilitário wsimport
A partir dos stubs gerados, é possível consumir o WebService como no exemplo abaixo:
package ExemplosDatasetService; import javax.xml.ws.BindingProvider; import net.java.dev.jaxb.array.StringArray; import com.datasul.technology.webdesk.dataservice.ws.DatasetDto; import com.datasul.technology.webdesk.dataservice.ws.DatasetService; import com.datasul.technology.webdesk.dataservice.ws.DatasetServiceService; import com.datasul.technology.webdesk.dataservice.ws.SearchConstraintDtoArray; import com.datasul.technology.webdesk.dataservice.ws.ValuesDto; /** * Classe para invocar serviço DatasetService. */ public class DataServiceClient { // Instancia DatasetServiceService. DatasetServiceService datasetServiceService = new DatasetServiceService(); DatasetService service = datasetServiceService.getDatasetServicePort(); // Inicia execução da classe. public static void main (String args[]) { System.out.println("\nClasse DatasetService"); // Instancia classe DatasetServiceClient. DataServiceClient dsc = new DataServiceClient(); // Configura acesso ao WebServices. BindingProvider bp = (BindingProvider) dsc.service; bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://localhost:8080/webdesk/DatasetService"); try { dsc.getDataset(); } catch (Exception e) { e.printStackTrace(); } } // Retorna dataset. public void getDataset() throws Exception { System.out.println("\nMétodo getDataset\n"); /* Cria os parâmetros utilizados na chamada */ StringArray fields = new StringArray(); StringArray order = new StringArray(); SearchConstraintDtoArray constraints = new SearchConstraintDtoArray(); /* Invoca o dataset */ DatasetDto result = service.getDataset(1, /* Empresa */ "adm", /* Usuario */ "adm", /* Senha */ "colleague", /* Dataset */ fields, /* Campos a Retornar*/ constraints, /* Condições */ order); /* Ordem dos campos */ /* 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 field : row.getValue()) System.out.print(field + "\t"); System.out.println(); } } }
Ao utilizar os WebServices via Java™, deve-se atentar para o tipo de cada atributo e para o tipo de retorno do WebService. Exemplos:
XMLGregorianCalendar: Utilizado ao passar valores do tipo “data”.
Exemplo de utilização: DocumentDto document = new DocumentDto(); XMLGregorianCalendar date; date = DatatypeFactory.newInstance().newXMLGregorianCalendar(); date.setYear(2009); date.setMonth(10); date.setDay(16); date.setHour(0); date.setMinute(0); date.setSecond(0); document.setExpirationDate(date);
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:
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, como na imagem abaixo:
Figura 8 - Exemplo de Documentação do WebService gerada pelo Progress
/* Parte I – Invocar o WebService */ DEFINE VARIABLE hWebService AS HANDLE NO-UNDO. DEFINE VARIABLE hDatasetService AS HANDLE NO-UNDO. DEFINE VARIABLE companyId AS INTEGER NO-UNDO. DEFINE VARIABLE username AS CHARACTER NO-UNDO. DEFINE VARIABLE password AS CHARACTER NO-UNDO. DEFINE VARIABLE name AS CHARACTER NO-UNDO. DEFINE VARIABLE fields1 AS CHARACTER EXTENT 0 NO-UNDO. DEFINE TEMP-TABLE item NO-UNDO NAMESPACE-URI "" FIELD fieldName AS CHARACTER FIELD finalValue AS CHARACTER FIELD initialValue AS CHARACTER . DEFINE DATASET constraints NAMESPACE-URI "http://ws.dataservice.webdesk.technology.datasul.com/" FOR item. DEFINE VARIABLE order AS CHARACTER EXTENT 0 NO-UNDO. DEFINE VARIABLE dataset1 AS LONGCHAR NO-UNDO. CREATE SERVER hWebService. hWebService:CONNECT("-WSDL 'http://norberto.datasul.net:8080/webdesk/DatasetService?wsdl'"). RUN DatasetService SET hDatasetService ON hWebService. /* Procedure invocation of getDataset operation. */ RUN getDataset IN hDatasetService(INPUT 1, INPUT "joyce", INPUT "adm", INPUT "colleague", INPUT fields1, INPUT DATASET constraints, INPUT order, OUTPUT dataset1). DELETE OBJECT hDatasetService. hWebService:DISCONNECT(). DELETE OBJECT hWebService. /* Parte II - Faz o parser do XML e criar um arquivo 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:\temp\saida.txt. CREATE X-DOCUMENT hDoc. hDoc:LOAD("longchar", dataset1, FALSE). CREATE X-NODEREF hRoot. hDoc:GET-DOCUMENT-ELEMENT(hRoot). CREATE X-NODEREF hEntry. CREATE X-NODEREF hText. CREATE X-NODEREF hValues. /* Percorre as colunas primeiro */ 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.
Via PHP
Para utilizar a integração com o PHP, primeiramente deve ser definido a estrutura DTO utilizado no WebService desejado, como um objeto array. Por exemplo, para utilizar SearchConstraintDto a seguinte estrutura deve ser utilizada no PHP:
$constraint = array("contraintType"=>"MUST", "fieldName"=>"<Valor do Campo>", "initialValue"=>"<Valor inicial>", "finalValue"=>"<Valor final>"); $constraintsDto = array("item"=>$constraint);
Um exemplo completo de utilização esta disponível no diretório <Diretório de instalação> \docs-ECM\Documentos de Customizacao\Arquivos Adicionais\Exemplos Webservices\ExemplosDatasetService\PHP\ getColleagueDataset.php
O TOTVS | ECM permite fazer chamadas a WebServices de terceiros através do cadastro de Serviços, no Painel de Controle.
Figura 9 - Cadastro de Serviços, no Painel Controle
Para adicionar um novo WebService, é preciso entrar na opção Adicionar e informar um nome para o serviço, o seu tipo (neste caso WebService), a sua descrição e a URL para o WSDL. No exemplo abaixo, será utilizado um WebService público para consulta à tabela periódica, cujo endereço do WSDL é http://www.webdtservicex.com/periodictable.asmx?wsdl.
Caso o servidor utilize proxy, deve ser configurado no arquivo run.bat para Windows ou run.sh para Linux na pasta bin os seguintes parâmetros:
Set JAVA_OPTS=%JAVA_OPTS% -Dhttp.proxyHost=<Servidor Proxy> -Dhttp.proxyPort=<Porta do Servidor Proxy> -Dhttp.nonProxyHosts="localhost|127.*" -Dhttp.proxyUser=<Usuário> -Dhttp.proxyPassword=<Senha> -Dhttp.auth.ntlm.domain=<Domínio> -Dhttp.proxySet=true -Dhttp.proxyDomain=<Domínio> -Dhttp.proxy.user=<Usuário> -Dhttp.proxy.password=<Senha>
É necessário que o servidor esteja no mesmo domínio do servidor proxy.
Figura 10 - Cadastro do WebService
Com base nestas informações, o TOTVS | ECM 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.
Figura 11 - Visualizando Classes e métodos do Serviço
A partir do cadastro do serviço, é possível instanciá-lo nos pontos onde o TOTVS | ECM é customizado utilizando-se JavaScript, como nos eventos de fichários, processos, ou no cadastro de Datasets. No exemplo a seguir, será criado um dataset que fará uso deste serviço para trazer os dados da tabela periódica. Para mais informações sobre o uso de datasets no TOTVS | ECM e como criar Datasets customizados, verifique a documentação técnica do produto.
O código abaixo apresenta uma implementação de exemplo do uso de um serviço na construção de um Dataset.
function createDataset(fields, constraints, sortFields) { // Cria o dataset var newDataset = DatasetBuilder.newDataset(); newDataset.addColumn("ElementName"); // Conecta o servico e busca os livros var periodicService = ServiceManager.getService('PeriodicTable'); var serviceHelper = periodicService.getBean(); var serviceLocator = serviceHelper.instantiate('NET.webserviceX.www.PeriodictableLocator'); var service = serviceLocator.getperiodictableSoap(); // Invoca o servico try { var retorno = service.getAtoms(); var NewDataSet = new XML(retorno); for each(elemento in NewDataSet.Table) { newDataset.addRow(new Array(elemento.ElementName.toString())); } } catch(erro) { newDataset.addRow(new Array(erro.message)); } return newDataset; }
O primeiro passo para invocar o serviço é solicitar do TOTVS | ECM que faça a carga do serviço, a partir do método ServiceManager.getService('PeriodicTable'). O nome passado como parâmetro, deve ser o mesmo nome utilizado no cadastro do Serviço, no Painel de Controle.
Uma vez que o serviço tenhasido 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 objetos disponíveis e que estão listadas no cadastro do Serviço, conforme a Figura 10.
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.
Observe na tabela abaixo, os passos exigidos pelo serviço da tabela periódica e como foi criado o código do dataset.
Passo | Código |
---|---|
Instanciar a classe NET.webserviceX.www.Periodicta bleLocator para ter acesso ao localizador de serviço | var serviceLocator = serviceHelper.instantiate('NET.webserviceX.www.PeriodictableLocator'); |
Invocar o método getperiodictableSoap para instanciar o serviço. | var service = serviceLocator.getperiodictableSoap(); |
Invocar o método getAtoms para ter a lista dos elementos. | var retorno = service.getAtoms(); |
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:
<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. Observe que as partes destacadas são correspondentes à estrutura do XML retornado.
var NewDataSet = new XML(retorno); for each(elemento in NewDataSet.Table) { newDataset.addRow(new Array(elemento.ElementName.toString())); }
Uma vez implementado o código do dataset, é possível visualizá-lo, conforme
a figura abaixo.
Figura 12 - Exemplo de dataset que utiliza um WebService
Assim como é possível invocar operações em WebServices, o TOTVS | ECM 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 TOTVS | ECM sem entrar em detalhes específicos das tecnologias envolvidas. A camada de serviço Progress® do TOTVS | ECM 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®.
Os exemplos exibidos a seguir, têm por objetivo a criação de quatro datasets1 no TOTVS | ECM:
1) Tipos de Centro de custo, que deve retornar os tipos de centro de custo existentes no aplicativo em Progress® (neste caso, o EMS2).
2) Natureza dos Centros de Custo, que deve retornar os tipos possíveis de natureza, conforme o aplicativo em Progress® (neste caso, o EMS2).
3) Centros de Custo, que deve retornar os registros na tabela conta2.
4) Usuários Comuns, que deve gerar uma lista de usuários comuns entre o TOTVS | ECM e o aplicativo em Progress® (utilizando a tabela usar_mestre).
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.
1 Os exemplos utilizam uma base dedados 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.
CostCenterUtils.p |
---|
/************************************************************************** |
No caso da extração de usuários comuns aos dois produtos, será utilizado um programa único, conforme o código abaixo:
verifyUsers.p |
---|
/************************************************************************** |
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.
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).
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.
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 abaixo3:
Figura 13 - ProxyGen -Nome do Projeto
Na primeira tela do ProxyGen o principal ponto que deve ser observado é o nome do Projeto (no exemplo acima, 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 TOTVS | ECM. Nesta tela também é preciso configurar o PROPATH corretamente, para que seja possível encontrar os arquivos compilados (.r).
3 Note que 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.
Figura 14 - ProxyGen -Inclusão de procedures
A segunda etapa 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.
Figura 15 - ProxyGen -Configurando nome do Serviço
Durante o processo de geração dos proxies, observe o campo AppService. Este deve ser o nome do serviço que deve ser publicado no AppServer, caso contrário não será possível conectar o servidor.
Figura 16 - ProxyGen -Definindo o package das classes
A última informação relevante para a geração dos proxies é o nome do pacote (package) onde as classes serão criadas. Esta informação é utilizada durante a configuração do serviço Progress® no TOTVS | ECM.
Uma vez criadas as classes, é preciso empacotá-las em um arquivo .JAR. Isto pode ser feito via linha de comando, utilizando-se o comando abaixo:
jar -cvf <jar_file_name> <diretório>
Observe apenas que no arquivo gerado, é preciso que as classes estejam nos diretórios corretos. No exemplo acima, o diretório com deve ser incluído e estar no raíz do arquivo Jar.
Por ser compatível com o formato zip, um opção é gerar um arquivo com as classes geradas (respeitando-se os diretórios) e renomeá-lo para a extensão .JAR.
Como mencionado anteriormente, a camada de serviço de acesso à Progress® no TOTVS | ECM é baseada nas bibliotecas Java Open AppServer Client, da Progress®. Uma vez que estas bibliotecas não são distribuídas no TOTVS | ECM, e como os arquivos que compem estas bibliotecas variam de acordo com a versão do Progress® utilizada, é preciso que o usuário faça a configuração destes ambientes no produto.
Para configurar o ambiente Progress® no TOTVS | ECM, é preciso identificar quais os arquivos .JAR que são exigidos para a execução dos proxies. A tabela abaixo apresenta o conjunto padrão de arquivos para as versões 9 e 10, embora ela possa sofrer variações dependendo do ambiente do usuário. Verifique a documentação do Progress® em cada versão para mais detalhes.
Progress®9.X | OpenEdge®10.X |
---|---|
o4glrt.jar | common.jar |
progress.jar | commonj.sdo.jar |
ecore.change.jar | |
ecore.jar | |
ecore.resources.jar | |
ecore.sdo.jar | |
ecore.xmi.jar | |
o4glrt.jar | |
openedge.jar | |
progress.jar |
ATENÇÃO: O conjunto de arquivos deve também ser copiado para a pasta <ECM>\server\default\lib. É necessário parar o serviço antes de efetuar a cópia e reiniciá-lo em seguida.
Não efetuando este procedimento pode ocorrer o erro: “java.lang.NoClassDefFoundError : com/progress/open4gl/SDOFactory” na execução dos serviços que utilizam este ambiente.
O TOTVS | ECM permite configurar mais de um ambiente Progress® em uma mesma instalação, desde que sejam fornecidos os arquivos .JAR de cada um destes ambientes. Note, entretanto que o ambiente deve ser configurado de acordo com a versão do ProxyGen utilizada para criação dos proxies e não necessariamente à versão do servidor de aplicação. Por exemplo, se os proxies foram gerados com o ProxyGen na versão 9, o ambiente deve ser configurado com esta versão, ainda que o servidor de aplicação esteja rodando com o OpenEdge® 10.
Para configurar um ambiente Progress®, acesse no painel de controle a opção “Ambientes Progress”, conforme a imagem abaixo:
Figura 17 - Tela de Configuração dos Ambientes Progress
Onde:
A configuração do Serviço Progress® no TOTVS | ECM permite isolar as informações de conexão aos servidor de aplicação da lógica que utiliza serviços nele disponibilizados. Com isto é possível alterar estas informações (como nome do servidor AppServer ou usuário de conexão), sem impactar as customizações que são baseadas neste serviço.
O cadastrado de um serviço é feito no Painel de Controle, na opção Serviços. A tela abaixo apresenta os campos utilizados para o cadastro do Serviço Progress®.
Figura 18 - Tela de cadastro do Serviço Progress
Onde:
No caso de não estar utilizando o NameServer padrão deve ser informada a porta de acesso ao NameServer. Ex: AppServer://<servidor>:<porta_NameServer>/<nome_serviço>.
Observe que dependendo da configuração do serviço e/ou da forma de conexão, a URL pode sofrer alterações. Verifique a documentação do Open AppServer para mais informações.
Uma vez que o serviço tenha sido configurado, é possível visualizar as classes disponíveis e os métodos existentes em cada 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, clique no botão visualizar no cadastro de Serviços, conforme a tela abaixo:
Figura 19 - Tela de Visualização de Classes e métodos do Serviço Progress
O acesso às procedures expostas no AppServer envolve quatro elementos que interagem entre si, conforme o diagrama abaixo:
Figura 20 - Visão geral de Classes
Onde:
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 programador 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 chamada ao método correspondente, por exemplo:
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 (verifique os métodos disponíveis da classe na opção Visualizar do cadastro de Serviços), ou via o método createManagedObject. A chamada via o método createManagedObject permite que o TOTVS | ECM tenha controle sobre o ciclo de vida deste objeto, liberando-o automaticamente ao fim do método. Caso o objeto seja instanciado manualmente, o programador 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.
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 consultadom na opção visualizar do cadastro de serviço. 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®.
Uma vez que o serviço tenha sido configurado no TOTVS | ECM, é possível utilizá-lo em qualquer ponto do produto onde sejam inseridos códigos JavaScript para execução no lado servidor (em especial, nas customizações de fichários, processos e na construção de 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 no produto e facilitar a reprodução dos exemplos apresentados no ambiente do cliente, 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”, “Usuários em Comum”.
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 a seguir:
function createDataset(fields, constraints, sortFields) { // Passo 1 - Recupera o serviço cadastrado no Painel de Controle var servico = ServiceManager.getService("ems2_v10"); // Passo 2 - Carrega o objeto utilitário para integração com Progress var serviceHelper = servico.getBean(); // Passo 3 - Carrega a procedure persistente CostCenterUtils.p var remoteObj = serviceHelper.createManagedObject("CostCenterUtils"); // Passo 4 - Invoca a procedure que retorna uma string com os tipos de CC var types = serviceHelper.createStringHolder(); remoteObj.readCostTypes(types); // Passo 5 - Quebra a string em um array com cada um dos tipos var typeArray = types.getStringValue().split(","); // Passo 6 - Cria o Dataset e adiciona cada tipo retornado var newDataset = DatasetBuilder.newDataset(); newDataset.addColumn("id"); newDataset.addColumn("descricao"); for(var pos = 0; pos < typeArray.length; pos++) { newDataset.addRow(new Array(pos + 1, typeArray[pos])); } return newDataset; }
Onde:
A tela abaixo apresenta a visualizaçào dos dados do Dataset criado.
Figura 21 - Visualização do Dataset de Tipos de Centros de Custo
O Dataset de Naturezas de Centro de Custo é muito similar ao dataset de tipo de centro de custo. Na prática, a última alteração é a procedure que é chamada.
function createDataset(fields, constraints, sortFields) { var servico = ServiceManager.getService("ems2_v10"); var serviceHelper = servico.getBean(); var remoteObj = serviceHelper.createManagedObject("CostCenterUtils"); // Lê os tipos de contas var types = serviceHelper.createStringHolder(); remoteObj.readCostNatureTypes(types); var typeArray = types.getStringValue().split(","); var newDataset = DatasetBuilder.newDataset(); newDataset.addColumn("id"); newDataset.addColumn("descricao"); for(var pos = 0; pos < typeArray.length; pos++) { newDataset.addRow(new Array(pos + 1, typeArray[pos])); } return newDataset; }
Após o cadastro do Dataset, é possível visualizar o seu conteúdo.
Figura 22 - Visualização do dataset de Naturezas de Centro 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 no Progress® 9 e OpenEdge® 10, respectivamente. Em ambos os casos, o resultado apresentado pelo Dataset será o mesmo.
As temp-table no Progress® 9 são tratadas através de objetos que implementam a interface java.sql.ResultSet.
function createDataset(fields, constraints, sortFields) { // Recupera o serviço e carrega o objeto remoto var servico = ServiceManager.getService("ems2_v9"); var serviceHelper = servico.getBean(); var remoteObj = serviceHelper.createManagedObject("CostCenterUtils"); // Lê as contas correntes var holder = serviceHelper.createResultSetHolder(); remoteObj.readCostCenters(holder); // Cria a estrutura do Dataset var newDataset = DatasetBuilder.newDataset(); newDataset.addColumn("conta"); newDataset.addColumn("titulo"); newDataset.addColumn("natureza"); newDataset.addColumn("tipo"); // 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"); newDataset.addRow(new Array(conta, titulo, natureza, tipo)); } return newDataset; }
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.
function createDataset(fields, constraints, sortFields) { var servico = ServiceManager.getService("ems2_v10"); var serviceHelper = servico.getBean(); var remoteObj = serviceHelper.createManagedObject("CostCenterUtils"); // Lê as contas correntes var holder = serviceHelper.createProDataGraphHolder(); remoteObj.readCostCenters(holder); var newDataset = DatasetBuilder.newDataset(); newDataset.addColumn("conta"); newDataset.addColumn("titulo"); newDataset.addColumn("natureza"); newDataset.addColumn("tipo"); var ttCC = holder.getProDataGraphValue().getProDataObjects("ttCC"); for(var row_index = 0; row_index < ttCC.size(); row_index++) { var row = ttCC.get(row_index); newDataset.addRow(new Array(row.get("conta"), row.get("titulo"), row.get("natureza"), row.get("tipo"))); } return newDataset; }
Figura 23-Visualização do Dataset de Centros de Custo
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 podemos observar neste caso é que é possível transformar um dataset nos tipos de dados requeridos pelo Progress® (ResultSet ou ProDataGraph).
function createDataset(fields, constraints, sortFields) { // Passo 1 – Recupera os usuários do TOTVS | ECM. var campos = new Array("colleaguePK.colleagueId", "colleagueName"); var dataset = DatasetFactory.getDataset("colleague", campos, null, null); // Passo 2 - Instancia o servico var servico = ServiceManager.getService("ems2_v9"); var serviceHelper = servico.getBean(); // Passo 3 - Transforma o dataset em um ResultSet (v9) e // cria holder para saida var inputTT = dataset.toResultSet(); var holder = serviceHelper.createResultSetHolder(); // Invoca a procedure no Progress serviceHelper.getProxy().verifyUsers(inputTT, holder); // Cria o novo Dataset var newDataset = DatasetBuilder.newDataset(); newDataset.addColumn("usuario"); newDataset.addColumn("nome"); var rs = holder.getResultSetValue(); while(rs.next()) { var cod_usuar = rs.getObject("cod_usuar"); var nom_usuario = rs.getObject("nom_usuario"); newDataset.addRow(new Array(cod_usuar, nom_usuario)); } return newDataset; }
unction createDataset(fields, constraints, sortFields) { // Primeiro, recupera os colaboradores correntes var campos = new Array("colleaguePK.colleagueId", "colleagueName"); var dataset = DatasetFactory.getDataset("colleague", campos, null, null); // Instancia o servico var servico = ServiceManager.getService("ems2_v10"); var serviceHelper = servico.getBean(); // Transforma o dataset em um ProDataGraph (v10) e cria holder para saida var inputTT = serviceHelper.toProDataGraph(dataset); var holder = serviceHelper.createProDataGraphHolder(); // Invoca a procedure no Progress serviceHelper.getProxy().verifyUsers(inputTT, holder); // Cria o novo Dataset var newDataset = DatasetBuilder.newDataset(); newDataset.addColumn("usuario"); newDataset.addColumn("nome"); var ttCC = holder.getProDataGraphValue().getProDataObjects("ttOutUsers"); for(var row_index = 0; row_index < ttCC.size(); row_index++) { var row = ttCC.get(row_index); newDataset.addRow(new Array(row.get("cod_usuar"), row.get("nom_usuario"))); } return
Figura 24 - Visualização do Dataset de Usuários Comuns
A tabela abaixo apresenta a lista de métodos existentes na classe utilitária para serviços Progress®:
Method Summary | |
---|---|
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo DECIMAL |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo LOGICAL |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo RAW |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo COM-HANDLE |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo DATE |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo WIDGET-HANDLE (Handle) |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo INTEGER |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo RECID |
java.lang.Object | createManagedObject(java.lang.String objName) |
Lê um arquivo .p ou .r que tenha sido exposto via appserver de forma persistente. | |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo MEMPTR |
java.lang.Object | createProDataGraph(java.lang.Object metadata)Cria um objeto da classe ProDataGraph |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo ProDataGraphHolder |
java.lang.Object | createBigDecimalHolder() Cria um objeto da classe ProDataGraphMetadata |
java.lang.Object | createProDataObjectMetaData(java.lang.String tableName, int numFields, boolean bimageFlag, int numIndexes, java.lang.String multiIxCols, java.lang.String XMLNamespace, java.lang.String XMLPrefix) Cria um objeto da classe ProDataObjectMetadata Cria um objeto para um dataset (Temp-table) |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo TABLE |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo ROWID |
java.lang.Object | createBigDecimalHolder() Cria um objeto Holder para o tipo CHARACTER |
java.lang.Object | getProxy() Retorna a instância do objeto de conexão ao appserver, já conectado e disponivel para uso. |
java.lang.Object | instantiate(java.lang.String className) Instancia um objeto de uma classe dentro da biblioteca do proxy. |
java.lang.Object | toProDataGraph(com.datasul.technology.webdesk.dataset.DefaultDataset d) Transforma um dataset em um ProDataGraph. |
Para facilitar o acesso a uma pasta no sistema, é utilizado um parâmetro na url do servidor. Sendo enviada a url <servidor>:<porta>/webdesk/ ?state=navigation&folderId=<Número da pasta ou do documento>
Exemplo:
http://localhost:8080/webdesk/?state=navigation&folderId=8
Importante: Esta situação só é válida para sessões que estejam autenticadas. Para sessões não autenticadas consulte o capítulo Acesso sem efetuar login, abaixo.
Para acessar um documento dentro de uma iframe do navegador, ou utilizando links de um documento para outro, pode ser utilizado o parâmetro ‘target’, para definir o destino da URL. Os valores do target são os mesmo do padrão HTML, podendo ser:
Exemplo:
http://localhost:8080/webdesk/documentviewer?WDNrDocto=44056&WDNrVersao=1000&target=_blank
É possível acessar o ECM externamente sem passar pela tela de login, basta utilizar as URL's informando o token de acesso. O token deve ser gerado utilizando o webservice TokenService do ECM. Para mais informações consultar o Guia de Referência Utilização de Webservices.
Os acessos disponíveis são:
ECM:
http://<servidor>:<porta>/josso/signon/ExternalLogin.do?t=<token gerado>&josso_cmd=true&josso_back_to=/webdesk/
Navegação posicionada em pasta:
http://<servidor>:<porta>/josso/signon/ExternalLogin.do?t=<token gerado>&josso_cmd=true&josso_back_to=/webdesk?state=navigation%26folderId=<número da pasta>
Aba de visualização do fluxo do processo
http://<servidor>:<porta>/josso/signon/ExternalLogin.do?t=<token gerado>&josso_cmd=true&josso_back_to=/webdesk/workflowstate?p=<código do processo>
Aba de visualização do fluxo do processo de uma solicitação aberta:
http://<servidor>:<porta>/josso/signon/ExternalLogin.do?t=<token gerado>&josso_cmd=true&josso_back_to=/webdesk/workflowstate?pi=<número da solicitação>