Árvore de páginas

Versões comparadas

Chave

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

Index:

 


Índice
outlinetruestylenone
exclude.*ndice
style
 
none


General Considerations

The information contained in this document is intended to demonstrate how to perform the integration between Fluig and external applications. In order to have a full understanding of this information, some knowledge is considered a prerequisite, including:

  • Overview of Fluig product
  • Overview of system integration
  • JavaScript
  • WebServices
  • SOAP
  • Progress® 4GL

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

In several parts of this document, code snippets will be presented in different programming languages in order to show the use of TOTVS Fluig Platform integration resources. However, this document does not intend to empower the reader to use these technologies beyond the above-described purpose, the reader being responsible for searching for more detailed information on these languages.

...

With the advent of Information System technologies, several systems began to provide support for these business processes, especially those considered most critical to company operation. The best example of this is the use of ERP systems that support the processes in various company areas.

TOTVS Fluig Platform has the objective of being an agnostic platform that manages processes, documents and identities through one collaborative communication interface. This can be realized to a greater or lesser degree in every one of its features, from the most simple (such as collaboration) to the more complex (such as DM and BPM).

However, part of these processes have a high reliance on existing information systems in the company and, therefore, TOTVS Fluig Platform's architecture is designed to enable integration with these systems, allowing the modeled processes to have higher added value.

TOTVS Fluig Platform allows both access through the product to external systems (to query or feed information) and enables other systems to connect to query information or to run transactional operations. 


...


The main product integration channel is through WebServices, which are becoming the most common standard for integration with any application. Through it, you can have access to TOTVS Fluig Platform features and provide access through the product to external applications. This document has a specific section on integration through WebServices.

The other form of integration is through calls to Progress® Open AppServer and is recommended for users who need to integrate TOTVS Fluig Platform with applications developed in that platform.

...

Nota

Watch out for each type of attribute that is expected, for example, the expirationDate attribute of the DocumentDto[] object is a date, however, each language interprets it differently, see some examples below:

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

 


Via Apache Flex®

As the vast majority of development tools, Apache Flex® allows you to create stubs to access to web services. These stubs include all the packing and unpacking operations of XML standard information for the platform native types.

Use the step-by-step to view the process of creating stubs for a service provided by Fluig:

Deck of Cards
historyfalse
idstubFlexhistoryfalse
Card
defaulttrue
effectDuration0.5
id1
labelStep 1
effectTypefade

The creation of stubs in Flex® is done through the Data menu, Import WebService(WSDL) option, as shown in the image below.

Card
effectDuration0.5
id2
labelStep 2
effectTypefade

In the first window, the folder within the current project where the stubs should be generated is requested.

Card
effectDuration0.5
id3
labelStep 3
effectTypefade

In the following screen, you should provide the WSDL address where the service is found. It is also possible to define if it will be accessed from the LifeCycle Data Services client or server station.

Card
effectDuration0.5
id4
labelStep 4
effectTypefade

In the last screen, you should provide the package that will be used and the name of the main class (already suggested by the Flex™ Builder™).

Card
effectDuration0.5
id5
labelResult
effectTypefade

When the process is finished, Flex™ Builder™ will add to the project a set of classes that will be used by the programmer to invoke the services, as shown in the figure below:

...

The code snippet below shows an example of the Fluig Dataset access WebService being called:

Bloco de código
languageactionscript3
themeEclipse
languagefirstlineactionscript31
titleECMDatasetServiceClient.mxmlfirstline1
linenumberstrue
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="{this.start()}">
	<mx:Script>
		<![CDATA[
			import generated.webservices.ValuesDto;
			import generated.webservices.DatasetDto;
			import generated.webservices.GetDatasetResultEvent;
			import generated.webservices.SearchConstraintDtoArray;
			import generated.webservices.StringArray;
			import generated.webservices.ECMDatasetServiceService;
			import mx.rpc.events.FaultEvent;
			import mx.collections.ArrayCollection;
		
			//Creates an instance of the service access Stub
			private var ds:ECMDatasetServiceService = new ECMDatasetServiceService();
			public function start() : void {
				//Creates assistant types that will be used on the service called
				var fields:StringArray = new StringArray();
				var constraints:SearchConstraintDtoArray = new SearchConstraintDtoArray();
				var order:StringArray = new StringArray();
				//Defines the functions for treating return
				ds.addEventListener(GetDatasetResultEvent.GetDataset_RESULT, resultGetDataset);
				ds.addEventListener(FaultEvent.FAULT,faultGetDataset);
				
				//invokes the service getDataset method
				ds.getDataset("adm", 1, "adm", constraints, order, fields, "colleague");
			}
			
			//Treatment of data returned from the invoked service.
			public function resultGetDataset(ev:GetDatasetResultEvent) : void {
				//Recovers service return in the form of a DatasetDto
				var dataset:DatasetDto = ev.result as DatasetDto;
				//Mounts a string with all the dataset data
				var line:String = "";
				
				//Header with the name of the fields
				var columnsArray:ArrayCollection = new ArrayCollection(dataset.columns);
				for (var j:int = 0; j < columnsArray.length; j++) {
					line += columnsArray.getItemAt(j) + "\t";
				}
				//Data line
				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";
					}
				}
				
				//Shows the string created in a textarea in the screen
				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

Flex® has a bug that prevents proper functioning of services that work with multidimensional arrays of data, as in the above example, where an array (of Dataset lines) of array (of the columns of each record) is returned.

To work around this problem, you must change the class generated by Flex™ Builder™, which will encapsulate the multidimensional array. In the above example, this class is the DatasetDto, which should be changed (line 11) as shown in the example below:

Bloco de código
languageactionscript3
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
}

Other services that do not work with multidimensional arrays do not require changes in the generated code.

 


Via Java™

There are many implementations of WebServices use in Java™ and, in this example, we will use the libraries available in Java™ 7.

...

From the generated stubs, it is possible to consume the WebService as in the example below:

Bloco de código
languagejava
themeEclipse
languagefirstlinejava1
titleECMDatasetServiceClient.javafirstline1
linenumberstrue
package com.fluig.examples;
import javax.xml.ws.BindingProvider;
import net.java.dev.jaxb.array.StringArray;
import com.totvs.technology.ecm.dataservice.ws.DatasetDto;
import com.totvs.technology.ecm.dataservice.ws.DatasetService;
import com.totvs.technology.ecm.dataservice.ws.ECMDatasetServiceService;
import com.totvs.technology.ecm.dataservice.ws.SearchConstraintDtoArray;
import com.totvs.technology.ecm.dataservice.ws.ValuesDto;
/*
 * Class to invoke the DatasetService service
 */
public class ECMDatasetServiceClient {
	//Instantiates DatasetServiceService.
	private ECMDatasetServiceService ecmDatasetServiceService = new ECMDatasetServiceService();
	private DatasetService service = ecmDatasetServiceService.getDatasetServicePort();
	
	//Starts class execution
	public static void main(String[] args) {
		ECMDatasetServiceClient client = new ECMDatasetServiceClient();
		
		//Configures access to 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 {
		
		//Creates the parameters used in the call
		int companyId = 1;
		String username = "adm";
		String password = "adm";
		String name = "colleague";
		StringArray fields = new StringArray();
		SearchConstraintDtoArray constraints = new SearchConstraintDtoArray();
		StringArray order = new StringArray();
		
		//Invokes the dataset service
		DatasetDto result = service.getDataset(
				companyId, username, password, name, fields, constraints, order);
		
		//Displays the header
		for (String columnName : result.getColumns()) {
			System.out.print(columnName + "\t");
		}
		System.out.println();
		
		//Displays the dataset lines
		for (ValuesDto row : result.getValues()) {
			for (Object value : row.getValue()) {
				System.out.print(value + "\t");
			}
			System.out.println();
		}
	}
}
Nota

When using WebServices via Java™, you must pay attention to the type of each attribute and to the type of return of the WebService. For example, for values of the date type, you should use the XMLGregorianCalendar class:

Bloco de código
languagejava
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

So, as in the previous examples, the first step to use a Webservice in Progress® is to use a utility that will read the ESDL address and generate the required information for accessing it. Different from Java™ and Flex®, Progress® does not generate stub objects, but only a documentation on how to consume the services described in the WSDL file. Although in some situations you can use the Progress® native types as parameters, depending on the data type used, you need to handle the SOAP XML to extract or send information.

...

The code below shows an example of how to use the service:

Bloco de código
languagejavafx
themeEclipse
languagefirstlinejavafx1
titlewsECMDatasetService.p
firstline1
linenumberstrue
/* Part I - Call 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.
/* Part II - Parse XML and create a text file separated by tabs */
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).
/* Goes through the columns <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.
/* Goes through the records <values> */
DO iCount = 1 TO hRoot:NUM-CHILDREN WITH 20 DOWN:
    hRoot:GET-CHILD(hValues, iCount).
    IF hValues:NAME <> "values" THEN
        NEXT.
    /* Goes through the fields <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.

...

The code below shows a sample implementation of the use of a service in the construction of a Dataset:

Bloco de código
firstline
languagejavascript
themeEclipse
languagefirstlinejavascript1
titleperiodicTable.js
1linenumberstrue
function createDataset(fields, constraints, sortFields) {
	//Creates the dataset
	var dataset = DatasetBuilder.newDataset();
	dataset.addColumn("elementName");
	// Connects the service and searches for the books
	var periodicService = ServiceManager.getService('PeriodicTable');
	var serviceHelper = periodicService.getBean();
	var serviceLocator = serviceHelper.instantiate('net.webservicex.Periodictable');
	var service = serviceLocator.getPeriodictableSoap();
	//Invokes the service
	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;
}

...

For the periodic table service, you need to perform the following steps:

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

...

In the case of this service, the getAtoms method returns a string containing an XML with the list of all elements, according to the example below:

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

...

The example below presents the code used to go through the XML returned:

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

...

In order to use WebServices that use basic authentication (WSS or WS-Security), you need to use the getBasicAuthenticatedClient method located in the service provider (the same obtained through the ServiceManager). This method provides an authenticated client. 


The parameters that should be provided in the method follow this order:

...

Using the example of the PeriodicTable service presented previously, the code of the call would have the changes below:

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

...

To customize the client that accesses the services, you need to use the getCustomClient method, located in the service provider (the same that is obtained via ServiceManager). This setting requires the creation of a map of parameters with their respective values to pass to the method, as show by the snippet below:

Bloco de código
languagejs
themeEclipse
languagejs
		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();

...


The following parameters can be set:

Property
Function
basic.authorization

When set to "true", it does the same as the getBasicAuthenticatedClient method, but allows you to apply authentication settings along with the other customizations below. In order to configure authentication, properties with "username" and "password" below must also be defined.

basic.authorization.username
User to be used for basic authentication.
basic.authorization.password
User password to be used for basic authentication.
disable.chunking

When set to "true", disables the sending of large requests in smaller "pieces". It can be useful when the service called does not support this type of request.

log.soap.messages
When set to "true", allows the SOAP messages used in requests made to services to be presented in the server log, making it easier to debug in case of failures.


Progress® Open AppServer

Thus since you can call operations in WebServices, Fluig also allows to make calls to programs in Progress® 4GL (or ABL) exposed through Progress® Open AppServer.

...

For the first three cases, the extraction logic of the desired information will be exposed in a program with several procedures, one for each need presented here:

Bloco de código
languagejavafx
themeEclipse
languagefirstlinejavafx1
titleCostCenterUtils.p
firstline1
linenumberstrue
/**************************************************************************
** Utility that provides procedures for extracting information
** on cost centers.
**************************************************************************/
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
   Objective: Returns a temp-table with the list of cost centers.
----------------------------------------------------------------------*/
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
   Objective: Returns a string with the natures of cost centers,
             separated by comma.
----------------------------------------------------------------------*/
PROCEDURE readCostNatureTypes:
    DEFINE OUTPUT PARAMETER cNatureList AS CHARACTER NO-UNDO.
    cNatureList = {adinc/i01ad047.i 03}.
END.
/*-------------------------------------------------------------------
  Procedure: readCostTypes
   Objective: Returns a string with the types of cost centers,
             separated by comma.
----------------------------------------------------------------------*/
PROCEDURE readCostTypes: 
    DEFINE OUTPUT PARAMETER cTypeList   AS CHARACTER NO-UNDO.
    cTypeList = {adinc/i02ad047.i 3}.
END.

In the case of extracting users that are common to both products, a single program will be used, as shown in the code below:

Bloco de código
languagejavafx
themeEclipse
languagefirstlinejavafx1
titleverifyUsers.pfirstline1
linenumberstrue
/**************************************************************************
** Utility that receives a temp-table with a list of users and returns
** another with only the users from the list that are in the database.
**************************************************************************/
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.

...

Use the step-by-step to view the process of creating the proxy: 


Deck of Cards
startHiddenfalse
effectDuration0.5
historyfalse
idproxyGenhistoryfalse
effectTypefade
 
Card
defaulttrue
id1
labelStep 1


  • In the first screen of ProxyGen, the main point that should be noted is the name of the Project (in this example, EMSProxies). The information in this field will be used by ProxyGen to name the service access class, which will be used to configure the service in Fluig. In this screen, you also need to correctly configure the PROPATH, so that you can search for the compiled files (.r).

 
Card
id2
labelStep 2


  • The second step consists in inserting which procedures will be exposed in a persistent or non-persistent way. The choice of each option to use depends on the way each exposed object was built. After the procedures are inserted, click the Generate option.

 


 
Card
id3
labelStep 3


  • During the process of generating the proxy, in the General tab, check the Java option in Client Proxy and enter the directory where the proxy is generated in the Output Dir. Also note the AppService field, it must be the name of the service posted in AppServer, otherwise it will be impossible to connect to the server.

Card
id4
labelStep 4


 

  • The last relevant information for the proxy generation is the name of the package where the classes will be created. This information is used during the configuration of the Progress® service in Fluig. Finally click on the OK button.

 

 


 
Card
id5
labelLast Step


  • After the classes are created, you need to package them in a .JAR file. This can be done in the command line using the command below:

Sem Formato
jar -cvf <jar_file_name> <diretorio>
 


However note that, in the file generated, the classes need to be in the correct directories. In the example shown, the com directory must be included and be at the root of the JAR file. To be compatible with the ZIP format, another option is to generate a file with the generated classes (respecting the directories) and rename it to the .JAR extension. 


Informações

Depending on the version of Progress®, the screens can suffer some variation in the amount and placement of fields. See the documentation in case of doubt

...

 



Service Configuration in Fluig

The registration of a service is performed through Fluig Studio, in the Services View through the Add Service option. The screen below shows the new service wizard and the fields used to register the Progress® service:

...


Where:

  • ServerFluig server where the service will be added;
  • Code: Unique code that will identify the service in the system. This code will be used in the JavaScript codes to have access to this service;
  • Description: Text that describes the data service;
  • URL: Identifies the AppServer service access URL, such as AppServer://<server>/<service_name>;
    In case you're not using the default NameServer, the NameServer access port must be provided. E.g.: AppServer://<server>:<NameServer_port>/<service_name>. 
    Note that depending on the service configuration and/or form of connection, the URL may change. Check the documentation of the Open AppServer for more information.
  • Type: Identifies the service type (Progress or WebService). Progress should be selected;
  • Remote Object: Identifies the proxy access class. This class is usually formed by the package name used for generating Proxy classes, plus the ProxyGen project name. 
    Example: In the screens that present the ProxyGen, we used the package com.fluig.samples.ems, and the name given to the project in ProxyGen was EMSProxies. In this case, the class of the remote Object will be com.fluig.samples.ems.EMSProxies;
  • User: User used when connecting to the service, as defined in the settings of the AppServer;
  • Password: Password used when connecting to the service, as defined in the settings of the AppServer;
  • Extra Parameters: Extra (and optional) parameters used for connecting to the AppServer. Check the documentation of the Open AppServer to check the options available in each version of the Progress®;
  • Proxy file directory: .JAR file containing the classes generated by ProxyGen; the Select File button should be used to find it.

...

Access to procedures presented in the AppServer involves four elements that interact with each other, as shown in the diagram below:

...


Where:

  • Script Code: is the JavaScript code that will make use of the procedures presented in the AppServer. As previously mentioned, this JavaScript can be of any nature, such as the implementation of a Dataset or the customization of a process event.
  • Service Provider: Object retrieved through ServiceManager.getService method that provides access to service features. This object is responsible for managing the connection and resources allocated by the service during script execution.
  • Service Helper: Object retrieved through the getBean method in the ServiceProvider and that provides a set of utility methods that allow, among other things, to create Progress® specific objects (StringHolder, ResultSetHolder, etc.), to have access to the ProxyGen remote object and to instantiate classes. For more information about the Service Helper, query here.
  • ProxyGen Classes: Classes generated by ProxyGen and that will be used by the developer for the Progress® routine execution. The list of available classes, as well as their methods, can be viewed in the Fluig Studio Service View.
 

Persistent and Non-Persistent Procedures

...

The procedures presented in a persistent way give rise to new classes that can be instantiated through calls to methods in the Remote Object (through the Service View in Fluig Studio, you can check the available methods in the class) or through the createManagedObject method. The call through the createManagedObject method allows Fluig to have control over this object life cycle, freeing it automatically at the end of the method. If the object is instantiated, the developer must manually encode the object release (_ release () method), otherwise there is the risk of blocking a new AppServer agent at each call to the method.

 


Input and Output Parameters

...

As previously seen, the Datasets that will be presented here are Cost Center TypesNature of the Cost CentersCost Centers and Users in Common. 


Cost Center Types

The code below shows the implementation of the Cost Center Types Dataset. The explanation of each step of the implementation will be presented after the code:

Bloco de código
languagejavascript
themeEclipse
languagefirstlinejavascript1
titledsTipoCentroCusto.jsfirstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {
	
	//Step 1 - Creates the dataset
	var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("id");
    dataset.addColumn("descricao");
    
	//Step 2 - Calls the service registered in Fluig
	var servico = ServiceManager.getService("EMS2");
 
	//Step 3 - Loads the utility object for integration with Progress
    var serviceHelper = servico.getBean();
 
	//Step 4 - Loads the CostCenterUtils.p persistent procedure
    var remoteObj = serviceHelper.createManagedObject("CostCenterUtils");
	//Step 5 - Calls the procedure that returns a string with the CC types
    var types = serviceHelper.createStringHolder();
    remoteObj.readCostTypes(types);
	//Step 6 - Breaks the string into an array with each of the types
    var typeArray = types.getStringValue().split(",");
 
	//Step 7 - Add each type returned
    for(var pos = 0; pos < typeArray.length; pos++) {
        dataset.addRow(new Array(pos + 1, typeArray[pos]));
    }
    return dataset;
}

...


Where:

  • Step 1: Creates the dataset and adds its fields;
  • Step 2: The registered service invocation is made in Fluig through the ServiceManager.getService method, and the value passed as parameter should be the service code. Note that at this point there's no need to provide any connection parameter in the service, since this has already been done in your registration;
  • Step 3: Use the getBean method to return a utility object for Progress® services. This utility provides a series of methods that facilitate interaction with the generated proxy and its methods will be presented in more details later in this document;
  • Step 4: Loads the CostCenterUtils object in a managed way through the createManagedObject method of the previously created utility;
  • Step 5: Invokes the method desired, in this case readCostTypes, passing a StringHolder which will receive the output value;
  • Step 6: Transforms the String received by the parameter in an array with the options. The , (comma) character is used to determine the breaking points of the string;
  • Step 7: Goes through the array created, adding a row in the Dataset for each item in the array. 

The screen below shows the Dataset data view created:

...


Nature of Cost Centers

The Nature of Cost Centers Dataset is very similar to the cost center type Dataset. In practice, the only change is the called procedure:

Bloco de código
languagejavascript
themeEclipse
languagefirstlinejavascript1
titledsNaturezaCentroCusto.js
firstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {
	var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("id");
    dataset.addColumn("descricao");
	var servico = ServiceManager.getService("EMS2");
	var serviceHelper = servico.getBean();
	var remoteObj = serviceHelper.createManagedObject("CostCenterUtils");
	var types = serviceHelper.createStringHolder();
	remoteObj.readCostNatureTypes(types);
    var typeArray = types.getStringValue().split(",");
    for(var pos = 0; pos < typeArray.length; pos++) {
        dataset.addRow(new Array(pos + 1, typeArray[pos]));
    }
    return dataset;
}

...


After Dataset registration, you can view its content:

...


Cost Centers

The Cost Centers Dataset has a structure that is very similar to the two Datasets previously seen. The main difference is that, in this case, the procedure returns a temp-table with cost centers, which changes the way data is handled.

...

The temp-table in Progress® 9 are treated through objects that implement the java.sql.ResultSet interface:

Bloco de código
languagejavascript
themeEclipse
languagefirstlinejavascript1
titledsCentroCustoP9.js
firstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {
    
	//Creates the structure of the Dataset
    var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("conta");
    dataset.addColumn("titulo");
    dataset.addColumn("natureza");
    dataset.addColumn("tipo");
    
	//Retrieves the service and loads the remote object
    var servico = ServiceManager.getService("EMS2");
    var serviceHelper = servico.getBean();
    var remoteObj = serviceHelper.createManagedObject("CostCenterUtils");
    
    //Reads the current accounts
    var holder = serviceHelper.createResultSetHolder();
    remoteObj.readCostCenters(holder);
    
    //Goes through the records, loading the Dataset with data
    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;
}

...

In OpenEdge® 10, the temp-tables returned are encapsulated as objects of the ProDataGraph class. This class is also used when you use parameters of the DATASET type:

Bloco de código
languagejavascript
themeEclipse
languagefirstlinejavascript1
titledsCentroCustoOE10.jsfirstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {
    
	//Creates the structure of the Dataset
    var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("conta");
    dataset.addColumn("titulo");
    dataset.addColumn("natureza");
    dataset.addColumn("tipo");
    
	//Retrieves the service and loads the remote object
    var servico = ServiceManager.getService("EMS2");
    var serviceHelper = servico.getBean();
    var remoteObj = serviceHelper.createManagedObject("CostCenterUtils");
    
    //Reads the current accounts
    var holder = serviceHelper.createProDataGraphHolder();
    remoteObj.readCostCenters(holder);
    
	//Goes through the records, loading the Dataset with data
    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;
}

...


Dataset View:

 


Users in Common

The first difference between the users in common Dataset and the previous examples is that, in this case, you must pass a temp-table as a parameter to the procedure called.

...

The third difference that can be observed in this case is that it is possible to transform a Dataset into the data types required by Progress® (ResultSet or ProDataGraph).

Progress® 9 Encoding


Bloco de código

...

code
language
javascript
themeEclipse
languagefirstlinejavascript1
titledsUsuariosComunsP9.js
firstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {
    
    //Creates the new Dataset
    var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("usuario");
    dataset.addColumn("nome");
    
	//Retrieves Fluig users
    var campos = new Array("colleaguePK.colleagueId", "colleagueName");
    var colleaguesDataset = DatasetFactory.getDataset("colleague", campos, null, null);
    
    //Instantiates the service
    var servico = ServiceManager.getService("EMS2");
    var serviceHelper = servico.getBean();
    
    //Transforms the dataset in a ResultSet (v9) and creates holder for the output
    var inputTT = colleaguesDataset.toResultSet();
    var holder = serviceHelper.createResultSetHolder();
    
    //Calls the procedure in 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;
}
OpenEdge® 10 Encoding


Bloco de código

...

code
language
javascript
themeEclipse
languagefirstlinejavascript1
titledsUsuariosComunsOE10.jsfirstline1
linenumberstrue
function createDataset(fields, constraints, sortFields) {
    
	//Creates the new Dataset
    var dataset = DatasetBuilder.newDataset();
    dataset.addColumn("usuario");
    dataset.addColumn("nome");
    
	//Retrieves Fluig users
    var campos = new Array("colleaguePK.colleagueId", "colleagueName");
    var colleaguesDataset = DatasetFactory.getDataset("colleague", campos, null, null);
    
    //Instantiates the service
    var servico = ServiceManager.getService("EMS2");
    var serviceHelper = servico.getBean();
	
    //Transforms the dataset in a ProDataGraph (v10) and creates holder for output
    var inputTT = serviceHelper.toProDataGraph(colleaguesDataset);
    var holder = serviceHelper.createProDataGraphHolder();
    
    //Calls the procedure in 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;
}

 


Dataset:

 

 



Service Helper

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

Retorno
Método e Descrição
java.lang.ObjectcreateBigDecimalHolder()
Cria um objeto Holder para o tipo DECIMAL
java.lang.ObjectcreateBooleanHolder()
Cria um objeto Holder para o tipo LOGICAL
java.lang.ObjectcreateByteArrayHolder()
Cria um objeto Holder para o tipo RAW
java.lang.ObjectcreateCOMHandleHolder()
Cria um objeto Holder para o tipo COM-HANDLE
java.lang.ObjectcreateDateHolder()
Cria um objeto Holder para o tipo DATE
java.lang.ObjectcreateHandleHolder()
Cria um objeto Holder para o tipo WIDGET-HANDLE (Handle)
java.lang.ObjectcreateIntHolder()
Cria um objeto Holder para o tipo INTEGER
java.lang.ObjectcreateLongHolder()
Cria um objeto Holder para o tipo RECID
java.lang.ObjectcreateManagedObject(java.lang.String objName)
Lê um arquivo .p ou .r que tenha sido exposto via AppServer de forma persistente. Através deste método o provedor do serviço pode gerenciar o ciclo de vida destes objetos, liberando-os ao final da execução do script.
java.lang.ObjectcreateMemptrHolder()
Cria um objeto Holder para o tipo MEMPTR
java.lang.ObjectcreateProDataGraph(java.lang.Object metadata)
Cria um objeto da classe ProDataGraph
java.lang.ObjectcreateProDataGraphHolder()
Cria um objeto Holder para o tipo ProDataGraphHolder
java.lang.ObjectcreateProDataGraphMetaData(java.lang.String name)
Cria um objeto da classe ProDataGraphMetadata
java.lang.ObjectcreateProDataObjectMetaData(java.lang.String tableName, int numFields, boolean bimageFlag, int numIndexes, java.lang.String multiIxCols, java.lang.String XMLNamespace, java.lang.String XMLPrefix)
Cria um objeto da classe ProDataObjectMetadata.
Cria um objeto para um dataset (Temp-table).
java.lang.ObjectcreateResultSetHolder()
Cria um objeto Holder para o tipo TABLE
java.lang.ObjectcreateRowidHolder()
Cria um objeto Holder para o tipo ROWID
java.lang.ObjectcreateStringHolder()
Cria um objeto Holder para o tipo CHARACTER.
java.lang.ObjectgetProxy()
Retorna a instância do objeto de conexão ao AppServer, já conectado e disponível para uso. O objeto remoto é a principal classe gerada pelo ProxyGen.
java.lang.Objectinstantiate(java.lang.String className)
Instancia um objeto de uma classe dentro da biblioteca do proxy.
java.lang.ObjecttoProDataGraph(com.datasul.technology.webdesk.dataset.DefaultDataset d)
Transforma um dataset em um ProDataGraph.