Páginas filhas
  • Intellector 9 - API - Criação de Plugin

Versões comparadas

Chave

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

...

Nem preciso explicar que esse framework nasceu da necessidade de padronizar o desenvolvimento dos primeiros plugins de acessos e execuções de políticas, embora essa característica fosse evidente desde o princípio. Na realidade, queríamos "forçar" qualquer desenvolvedor de plugins de acesso para o TOTVS Intellector a seguir o pattern que fosse operacional na chamada da política ao acesso plugin desenvolvido, como exceções lançadas, etc., e tivesse uma luz no fim do túnel, com algumas artifícios que ajudassem, pois quanto problemas e dúvidas melhor. Ele é um framework de abstração para operações como chamadas HTTPS com certificados, as vezes bem complexas, e, acreditem, não é tão trivial lidar com sopa de letrinhas esquisitas. Mas nada de ficar assustado, abaixo veremos cada característica desse framework.

  • Interface Plugin - Interface de acessos externosplugins externos. Arremessa as exceções 'InfraException' para problemas de infraestrutura, como unknown host (conexões e suas configurações, erros de autenticação, logins e passwords, jks, etc);'LayoutException' para problemas relacionados aos layouts, registros e tipos do acesso plugins e, 'ConfigException' se for detectado qualquer anomalia na configuração do acessoplugin. Atualmente um único método precisa ser implementado, oexecute(). É interessante observar as HashMap de entrada/saída:

...

  1. CipheringException - Classe Exception para tratamento das exceptions de ciphering (criptografia);
  2. ConfigException - Exceção gerada sempre que há um erro na configuração do acesso plugin externo, como por exemplo, arquivos de configuração não localizados, certificados não localizados, etc.;
  3. InfraException - Exceção gerada sempre que há um erro na execução do acesso plugin externo, como por exemplo, host não localizado, erros na conexão, autenticação, etc.;
  4. LayoutException - Exceção gerada sempre que há um erro na configuração dos layouts, como por exemplo, arquivos de configuração não localizados, layouts configurados de forma errada, etc.;
  5. PolicyException - Exceção gerada sempre que há um erro na política de controle de acessos de usuários na publicação de políticas, layouts de políticas, autenticação no site e execução de políticas.

...

Arquivos de configuração do Plugin e para a sua geração

São dois os arquivos que deverão ser criados para a configuração e geração do plugin (os arquivos de exemplo estão no projeto Eclipse com fontes que são disponibilizados mais abaixo):

Criar manualmente o arquivo com Criar manualmente os arquivos com layout do plugin externo e outras informações. Um template para a criação desse arquivo se encontra no dummyplugin para facilitar a vida do desenvolvedor e, tenha em mente, ele será validado pelo Intellector IW-Editor. Esse JSON deverá conter o nome do plugin externo e a sua classe de implementação em Java, lista das variáveis de entrada e outra de saída com o nome, tipo Java de cada uma, formato e descrição, e.g. abaixo para o nosso dummyplugin: 

São dois os arquivos que deverão ser criados para a configuração e geração do plugin (os arquivos de exemplo estão no projeto Eclipse com fontes que são disponibilizados mais abaixo):

  • JSON - arquivo com as definições da classe implementada pelo plugin, lista de variáveis de entrada e saída do plugin, propriedades que o usuário poderá alterar.
  • POM.XML - Além das definições de armazento do plugin em um repositório contém informações sobre a geração do plugin.

JSON

Bloco de código
languagehtml/xml
linenumberstrue
<?xml version="1.0" encoding="UTF-8"?>
<dummy>
	<!-- metodo de acesso para ser carregado no acesso 		-->
	<code name="br.com.tools.acessos.DummyAccess"/>

	<!-- esse deverah ser sufixo para ser acrescentado ao nome 	-->
	<!-- vindo da politica, entao irei buscar na hash da politica	-->
	<!-- hash.getKey(cpf_dummy); um de/para para os elementos 	-->
	<nome_acesso>dummy</nome_acesso>

	<!-- contem os dados necessarios para entrada no Dummy  	-->
	<entrada>
		<!-- posso testar pelo valor obrigatorio dentro de cada -->
		<!-- acesso,  ele dever ser "CPF" 			-->
		<field description="cpf requerente" type="String" format="">CPF</field>
		<field description="Data de Nascimento" type="Date" format="ddmmyyyy">DT_NASC</field>
	   	<field description="Tem seguro" type="Boolean" format="">TEM_SEGURO</field>
	   	<field description="Valor do salario" type="Double" format="">VAL_SALARIO</field>
	   	<field description="Idade do requerente" type="Integer" format="">IDADE</field>
	</entrada>

	<!-- contem todas as saidas disponiveis pelo Dummy		-->
	<!-- Obs.: quando counter=alguma_coisa, entao todo o bloco 	-->
	<!-- abaixo sofrerah um looping baseado nesse counter		-->   	
	<saida id="DUMMY" counter="" >
	   	<register description="Teste de boolean" type="Boolean" format="">BOOLEAN_VALUE</register>
	   	<register description="Teste pra tipo Data" type="Date" format="">DATE_VALUE</register>
	   	<register description="Teste pra tipo Double" type="Double" format="">DOUBLE_VALUE</register>
	   	<register description="Teste pra tipo Integer" type="Integer" format="">INTEGER_VALUE</register>
	   	<register description="Teste pra tipo String" type="String" format="">STRING_VALUE</register>
	</saida>
   	<saida id="D100" counter="" >
		<register description="Um Nome qualquer" type="String" format="">D100_NOME</register>
		<register description="Uma data de Nascimento" type="Date" format="ddmmyyyy">D100_DTNASCIMENTO</register>
		<register description="Mostra o diretorio" type="String" format="">D100_MYDIR</register>
		<register description="Qualquer string" type="String" format="">D100_OUTRO</register>
	</saida>
	<saida id="D200" counter="D200_NUMCONSULTAS" >
		<register description="Simula String com contador" type="String" format="">D200_TIPO_</register>
		<register description="Simula Date com contador" type="Date" format="yyyyddmm">D200_DATA_</register>
		<register description="Simula String com contador" type="String" format="">D200_HORA_</register>
		<register description="Simula String com contador" type="String" format="">D200_MOEDA_</register>
		<register description="Simula Double com contador" type="Double" format="">D200_VALOR_</register>
   	</saida>
</dummy>
E o dummyaccess não é tão dummy assim...

O acesso dummy estará sempre sendo submetido a melhorias, por isso, procure sempre por uma versão atualizada, pois ele terá várias versões, que ao longo do nosso desenvolvimento são taggeds. Talvez exista uma que seja mais adequado a sua necessidade.

Desenvolvendo...

...

{
"class_name": "br.com.totvs.plugins.dummyplugin.DummyPlugin" ,
"plugin_name": "dummyplugin",
"description":"Descrição do Plugin",
"layout": {
"editable": true,
"in": [
{
"description": "cpf requerente",
"type": "String",
"name": "CPF",
"format": ""
},
{
"description": "Data de Nascimento",
"type": "Date",
"name": "DT_NASC",
"format": "ddMMyyyy"
},
{
"description": "Tem seguro",
"type": "Boolean",
"name": "TEM_SEGURO",
"format": ""
},
{
"description": "Valor do salario",
"type": "Double",
"name": "VAL_SALARIO",
"format": ""
},
{
"description": "Idade do requerente",
"type": "Integer",
"name": "IDADE",
"format": ""
}
],
"out": [
{
"id": "DUMMY",
"counter": 0,
"counter_max": 0,
"register": [
{
"description": "Teste de boolean",
"type": "Boolean",
"name": "BOOLEAN_VALUE",
"format": ""
},
{
"description": "Teste pra tipo Data",
"type": "Date",
"name": "DATE_VALUE",
"format": "ddMMyyyy"
},
{
"description": "Teste pra tipo Double",
"type": "Double",
"name": "DOUBLE_VALUE",
"format": ""
},
{
"description": "Teste pra tipo Integer",
"type": "Integer",
"name": "INTEGER_VALUE",
"format": ""
},
{
"description": "Teste pra tipo String",
"type": "String",
"name": "STRING_VALUE",
"format": ""
}
]
},
{
"id": "D100",
"counter": 0,
"counter_max": 0,
"register": [
{
"description": "Um Nome qualquer",
"type": "String",
"name": "D100_NOME",
"format": ""
},
{
"description": "Uma data de Nascimento",
"type": "Date",
"name": "D100_DTNASCIMENTO",
"format": "ddMMyyyy"
},
{
"description": "Mostra o diretorio",
"type": "String",
"name": "D100_MYDIR",
"format": ""
},
{
"description": "Qualquer string",
"type": "String",
"name": "D100_OUTRO",
"format": ""
}
]
},
{
"id": "D200",
"counter": 2,
"counter_max": 3,
"register": [
{
"description": "Simula String com contador",
"type": "String",
"name": "D200_TIPO_",
"format": ""
},
{
"description": "Simula Date com contador",
"type": "Date",
"name": "D200_DATA_",
"format": "yyyyddMM"
},
{
"description": "Simula String com contador",
"type": "String",
"name": "D200_HORA_",
"format": ""
},
{
"description": "Simula String com contador",
"type": "String",
"name": "D200_MOEDA_",
"format": ""
},
{
"description": "Simula Double com contador",
"type": "Double",
"name": "D200_VALOR_",
"format": ""
}
]
}
]
},
"properties": {
"database": "mango_d",
"usuario": "claudio",
"senha": "mariposa",
"audita": true
}
}

POM.XML

Bloco de código
languagehtml/xml
linenumberstrue
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>grouo-id</groupId>
	<artifactId>dummyplugin</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<description>Dummy para exemplificar plugins externos</description>

	<properties>
		<!-- nome da package e classe - FQN -->
		<main-class>br.com.totvs.plugins.dummyplugin.DummyPlugin</main-class>
		<!-- Nome do Plugin -->
		<pluginname>dummyplugin</pluginname>
		<description>Dummy para exemplificar plugins externos</description>
		<!-- chave de acesso ao dado na persistencia -->
		<primarykey>CPF</primarykey>
		<pkdescription>CPF do adquirente</pkdescription>
		<!-- Caminho para o Layout do Plugin -->
		<layoutpath>resources/dummyplugin.json</layoutpath>
		
		<!-- P - plugin /  L - lib para plugin -->
		<pluginlib>P</pluginlib>
	</properties>

	<distributionManagement>
		<repository>
			<id>releases</id>
			<url>http://repo.com:8081/nexus/content/repositories/releases</url>
		</repository>
		<snapshotRepository>
			<id>snapshots</id>
			<url>http://repo.com:8081/nexus/content/repositories/snapshots</url>
		</snapshotRepository>
	</distributionManagement>

	<build>
		<sourceDirectory>src</sourceDirectory>
		<resources>
			<resource>
				<directory>src</directory>
				<excludes>
					<exclude>**/*.java</exclude>
				</excludes>
			</resource>
			<resource>
				<directory>resources</directory>
				<includes>
					<include>**/*.*</include>
				</includes>
				<targetPath>resources</targetPath>
			</resource>
		</resources>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.5.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>3.0.2</version>
				<configuration>
					<archive>
						<manifest>
							<addClasspath>false</addClasspath>
							<mainClass>${main-class}</mainClass>
						</manifest>
						<manifestSections>
							<manifestSection>
								<name>Intellector Entries</name>
								<manifestEntries>
									<Implementation-Plugin>${pluginname}</Implementation-Plugin>
									<Implementation-Layout>${layoutpath}</Implementation-Layout>
									<Implementation-Description>${description}</Implementation-Description>
									<primarykey>${primarykey}</primarykey>
									<pkdescription>${pkdescription}</pkdescription>
									<Plugin-Lib>${pluginlib}</Plugin-Lib>
								</manifestEntries>
							</manifestSection>
						</manifestSections>
					</archive>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-source-plugin</artifactId>
				<version>2.2.1</version>
				<executions>
					<execution>
						<id>attach-sources</id>
						<phase>verify</phase>
						<goals>
							<goal>jar-no-fork</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<artifactId>maven-clean-plugin</artifactId>
				<version>3.0.0</version>
				<executions>
					<execution>
						<!-- Aqui estou forçando um clean toda vez que o maven install for 
							executado. -->
						<id>auto-clean</id>
						<phase>initialize</phase>
						<goals>
							<goal>clean</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
		<pluginManagement>
			<plugins>
				<!--This plugin's configuration is used to store Eclipse m2e settings 
					only. It has no influence on the Maven build itself. -->
				<plugin>
					<groupId>org.eclipse.m2e</groupId>
					<artifactId>lifecycle-mapping</artifactId>
					<version>1.0.0</version>
					<configuration>
						<lifecycleMappingMetadata>
							<pluginExecutions>
								<pluginExecution>
									<pluginExecutionFilter>
										<groupId>org.apache.maven.plugins</groupId>
										<artifactId>maven-clean-plugin</artifactId>
										<versionRange>[2.5,)</versionRange>
										<goals>
											<goal>clean</goal>
										</goals>
									</pluginExecutionFilter>
									<action>
										<ignore></ignore>
									</action>
								</pluginExecution>
							</pluginExecutions>
						</lifecycleMappingMetadata>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>
	<dependencies>
		<dependency>
			<groupId>iw-server</groupId>
			<artifactId>iw-server-api</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>iw-server</groupId>
			<artifactId>iw-server-beans</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>
	</dependencies>
</project>


E o dummyaccess não é tão dummy assim...

O plugin dummy estará sempre sendo submetido a melhorias, por isso, procure sempre por uma versão atualizada, pois ele terá várias versões, que ao longo do nosso desenvolvimento são taggeds. Talvez exista uma que seja mais adequado a sua necessidade.

Desenvolvendo...

Importar o plugin dummy (dummyplugin) como um template para desenvolvimento de um plugin externo. A ideia é que ele oriente o desenvolvimento de toda a estruturanecessária para a criação de um plugin de acesso, mantendo uma padronização na construção destes, evitando que o fonte de cada plugin externo tenha uma forma de implementação completamente diferente dos outros. O objetivo é facilitar desenvolvimento de novos plugins de forma fácil e rápida. Os artefatos que comporão o projeto vazio de um plugin serão:

    • No arquivo JSON:
      • Definir as propriedades com os dados de configuração do plugin externo, caso existam. Como não se sabe inicialmente quais são esses dados, definir no arquivo JSON de exemplo as propriedades mais comuns: protocolo (HTTP, HTTPS, TCP, etc), host, porta e objeto no servidor; atualmente só http/servlet é suportado. Essas propriedades poderão ser configuradas na interface do Intellector na edição do plugin.
        • "properties": {
          "database": "mango_d",
          "usuario": "user",
          "senha": "senha",
          "audita": true
          }
      • Definir os dados de entrada para chamada do plugin de acordo com o layout pré estabelecido pelo órgão e, que receberá o layout montado pela classe anterior e fará o acesso e retornará a classe com o layout de saída;
    • As estruturas das classes Java que deverão ser programadas para cada plugin externo:
    • As classes, se for o caso, de implementação do plugin externo, para que o desenvolvedor possa programar o plugin externo propriamente dito;
    • Uma classe que fará o parser do retorno do órgão externo e montará o VO com as variáveis de resposta, que será chamada pela classe que efetuou o acesso;
    • Parametrizar no arquivo POM.XML as propriedades necessárias para correta geração do plugin. Essas confiogurações serã utilizadas para, por exemplo, a geração do META-INF/MANIFEST.MF.
      • <main-class> : classe principal do plugin, ou seja, a classe a ser instanciada pelo Intellector Server. 
      • <pluginname> : nome do plugin
      • <description> : uma descrição do plugin
      • <primarykey> : chave a ser usada para a persistência da execução do plugin. Normalmente é uma das variáveis de entrada do plugin e será usada como referência do uso de cache.
      • <pkdescription>: descrição da chave
      • <layoutpath>: caminho para o arquivo json de configuração do plugin.
      • <pluginlib> : indicação se é um plugin ou uma lib para uso por outros plugins.  P - plugin / L - lib para plugin
    • Para consultar o plugin de acesso é necessário determinar as chaves primárias para que futuramente o plugin de acesos seja armazenado em uma base de dados, portanto as variáveis primarykey e pkdescription deverão ser informados
  • Arquivo de propriedades com os dados de configuração do acesso externo, caso existam. Como não se sabe inicialmente quais são esses dados, criar um arquivo de exemplo com as propriedades mais comuns: protocolo (HTTP, HTTPS, TCP, etc), host, porta e objeto no servidor; atualmente só http/servlet é suportado;
  • Artefatos necessários para a criação de uma funcionalidade no site do Intellector Server que permitirá a configuração do arquivo de propriedades pelo administrador do sistema;
  • As estruturas das classes Java que deverão ser programadas para cada acesso externo:
  • As classes, se for o caso, de implementação do acesso externo, para que o desenvolvedor possa programar o acesso externo propriamente dito;
  • Um XML com os dados de entrada para chamada do acesso de acordo com o layout pré estabelecido pelo órgão e, que receberá o layout montado pela classe anterior e fará o acesso e retornará a classe com o layout de saída;
  • Uma classe que fará o parser do retorno do órgão externo e montará o VO com as variáveis de resposta, que será chamada pela classe que efetuou o acesso;
  • Colocar todos os arquivos de propriedades e layouts dentro de uma pasta, para que seja retirado do JAR depois. Essa lista deve ficar no arquivo de manifesto, i.e. META-INF/MANIFEST.MF com uma tag específica (Implementation-FileList - veja exemplo); os arquivos importados ficarão no diretório apontado pela variável de ambiente descrita pela tag 'Implementation-Datadir' no MANIFEST.MF e, esse local deve ter permissão de escrita para o owner do application server;
  • Para configuração do plugin de acesso no "Intellector Data Dir", deverá ser informado
    • no arquivo de manifesto, i.e. META-INF/MANIFEST.MF,
    o nome do acesso, na tag 'Implementation-Plugin'. Na Instalação do plugin, no Intellector, será criada um diretório com esse nome no "Intellector Data Dir".
  • Para consultar o plugin de acesso é necessário determinar as chaves primárias para que futuramente o plugin de acesos seja armazenado em uma base de dados, portanto as variáveis primarykey e pkdescription deverão ser informados no arquivo de manifesto, i.e. META-INF/MANIFEST.MF, sendo o campo primarykey as chaves no HashMap de entrada do acesso mais importantes da consulta separados por ',' para que sejam consultados na funcionalidade Consultar Resultados de Plugin de Acesso e o campo pkdescription um nome mais amigável para que seja listado na funcionalidade também separado por ',' na mesma sequência descrita no campo primarykey;
  • Criar um novo projeto no Eclipse, tendo como base/exemplo o acessodummy no diretório base de onde os artefatos foram gerados.
    • sendo o campo primarykey as chaves no HashMap de entrada do plugin mais importantes da consulta separados por ',' para que sejam consultados na funcionalidade Consultar Resultados de Plugin e o campo pkdescription um nome mais amigável para que seja listado na funcionalidade também separado por ',' na mesma sequência descrita no campo primarykey;
    • Criar um novo projeto no Eclipse, tendo como base/exemplo o dummyplugin no diretório base de onde os artefatos foram gerados.
    • É obrigatório informar no layout de saída nas variáveis  XXX e  YYYY a string que foi enviada no request e a string que foi retornada do request. Caso o plugin não tenha essas informações deixe-as vazias.
      • hashOut.put("LPT__PLUGIN_ENVIO", "STRING REQUEST");
      • hashOut.put("LPT__PLUGIN_RETORNO", "STRING RETORNO");

Exemplo do MANIFEST.MF Exemplo do MANIFEST.MF (nesse caso, um do SERASA) : 

Bloco de código
languagehtml/xml
linenumberstrue
Manifest-Version: 1.0
 Main-Class: br.com.tools.acessos.serasa.SerasaPF
 Class-Path: .
 Version-Info: teste-10
 Implementation-Vendor: Tools Servicos
 Implementation-Plugin: serasapf
 Implementation-Layout: serasapf
 Implementation-Datadir: serasapf.datadir
 Implementation-FileList: 
  resources/https.properties,
  resources/layout_p002.xml,
  resources/layout_p006.xml,
  resources/layoutPF_b49c.xml,
  resources/layoutPJ_b49c.xml,
  resources/serasa.properties,
  resources/serasapf.xml,
  resources/serasapj.xml
 primarykey: CPF, DTNASCIMENTO,
 pkdescription: CPF, Data de Nascimento,
Downloads
true
Manifest-Version: 1.0
Built-By: ricart.monachesi
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_202
Main-Class: br.com.totvs.plugins.dummyplugin.DummyPlugin

Name: Intellector Entries
Implementation-Description: Dummy para exemplificar plugins externos
Plugin-Lib: P
pkdescription: CPF do adquirente
Implementation-Plugin: dummyplugin
Implementation-Layout: resources/dummyplugin.json
primarykey: CPF
Downloads

Baixe dummyplugin.7z o template do dummypluginBaixe aqui o template do accessdummy