# Como desenvolver um Plugin Externo #

Este tutorial descreve o passo-a-passo de como desenvolver um Plugin Externo e integrá-lo
ao Intellector, fazendo importação ou exportação, layouts, configurando, executando e verificando
as saídas do plugin. O tutorial segue uma implementação real e esse arquivo pertence aos fontes.


### As "partes" que compõe o Plugin Externo ###

* A classe principal que implementa a __PluginInterface__
* Montagem das informações no _pom.xml_ para serem incluídas no **MANIFEST.MF**
* O Layout de informações primordiais (variáveis entrada/saída) e outras propriedades

### Componentes para a construção da Classe principal ###

Para a implementação da classe principal, ela deve implementar a interface __PluginInterface__, que
contém um único método _execute()_, que por sua vez deve lançar três exceções controladas pelo
chamador do plugin, __InfraException__, __LayoutException__, __ConfigException__.

* Inclusão de dependências do Maven onde contém as interfaces, exceções e API de acesso ao BD para as propriedades do _plugin_

```
#!xml
	<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>

```

* PluginInterface 

```
#!java
public interface PluginInterface {
	/**
	 * Executa um acesso externo; interface
	 * <p>
	 * 
	 * @param hashIn
	 * @throws InfraException
	 * @throws LayoutException
	 * @throws ConfigException
	 */
	public Map<String, Object> execute(Map<String, Object> hashIn) throws InfraException, LayoutException, ConfigException;
}

```

* Exception de Infra, as outras duas _exceptions_ seguem o modelo abaixo:

```
#!java
public class InfraException extends Exception {
	/**
	 * 
	 */
	private static final long serialVersionUID = 5665041800829856534L;

	/**
	 * Mostra uma excecao dos problemas de infreestrutura, comunicacao
	 * <p>
	 * 
	 * @param msg
	 */
	public InfraException(String msg) {
		super(msg);
	}

	/**
	 * Constroi uma nova excecao especificando a mensagem detalhada e a causa.
	 * Note que a mensagem detalhada associada a causa nao sao incorporadas
	 * automaticamente na mensagem desta excepcao.
	 * 
	 * @param message Mensagem detalhada (que eh armazenada para posterior obtencao
	 *            pelo metodo {@link #getMessage()})
	 * @param cause Causa (que eh guardada para posterior recuperacao pelo metodo {@link #getCause()}) 
	 * 			 (Um valor nulo eh permitida, e indica que a causa eh inexistente ou desconhecida.)
	 */
	public InfraException(String message, Throwable cause) {
		super(message, cause);
	}
}

```

### Construção da Classe Principal ###

O escopo principal da classe de implementação é algo muito simples, bastando implementar a interface como o código abaixo:

```
#!java
public class DummyPlugin implements PluginInterface {

	/**
	 * Pega o nome do Plugin Externo; especializado em pegar o nome do acesso
	 * @param hashIn Um map com as variaveis de entrada e seus valores
	 * @return hashOut Um map com as variaveis de saida do plugin
	 * @exception InfraException, LayoutException, ConfigException, arremessa uma de. 
	 */
	public Map<String, Object> execute(Map<String, Object> hashIn) throws InfraException, LayoutException, ConfigException {

```
* Sobre _Logging_
Devido a injeção em tempo de execução no Intellector do _plugin_, do qual fará uso do escopo do server, algumas API devem ser observadas
e usadas obrigatoriamente, ou mesmo verificar se existe uma determinada API no escopo do __Intellector Server__ para que seja 
utilizada, ou teremos uma dependência quebrada. Para _logging_ o Intellector usa o __log4j__ conforme código abaixo:
```
#!java
// -----------
// IMPORTANTE! A dependecia log4j jah disponivel no package, use-o!
// -----------
Logger log = Logger.getLogger(DummyPlugin.class);
log.info(">> Executando acesso 'DummyPlugin'...");

```
* Sobre _JSON_
Para mapeamento e uso de JSON, o Intellector Server dispõe do __Jackson Json__, usado em toda a aplicação, dispensando o
uso de XMLs, veja abaixo um exemplo:
```
#!java
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> config = mapper.readValue(jsonOut, new TypeReference<Map<String,Object>>(){});

```
* Verificações extras no plugin de exemplo, como abaixo verificando se foi informado uma determinada tag no map() de entrada
```
#!java
// verifica a existencia dos campos de entrada no map do acesso
String cpf = (String) hashIn.get("CPF");
if (cpf == null) {
	log.error("Nao tem CPF na hash de entrada");
	throw new LayoutException("Faltando 'CPF' na hash de entrada");
}
log.debug(">>DummyPlugin.execute: CPF [" + cpf + "]");
```

* Finalmente, preencher um __Map<String, Object>__ com as variáveis de saída.
```
#!java
// map para retornar os registros formatados <K,V> para quem me chamou
Map<String, Object> hashOut = null;
...
hashOut = new HashMap<String, Object>();
// insere na hash de saida uma variavel de cada tipo tratado na politica de credito
hashOut.put("BOOLEAN_VALUE", Boolean.TRUE);
...
// devolve a hash
return hashOut;

```


### Construção do layout do Plugin ###

* Considero a coisa mais importante para a construção do _plugin_, pois toda a __mecânica__ da aplicação ao
executar o _plugin_, se baseia nesse __layout__, então veremos as possibilidades, e as convenções a serem usadas e
detalhes a serem observados. Pra iniciar, abaixo o layout construído para o nosso Dummy, então continuaremos na
sequência a explicação de cada item.
```
#!json
{
  "class_name": "br.com.totvs.plugins.dummyplugin.DummyPlugin" ,
  "nome_acesso": "dummyplugin",
  "dummyplugin_layout": {
  	"editavel": false,
    "entrada": [
        {
          "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": ""
        }
      ],
    "saida": [
      {
        "id": "DUMMY",
        "counter": 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,
        "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,
        "register": [
          {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_TIPO_1",
            "format": ""
          },
          {
            "description": "Simula Date com contador",
            "type": "Date",
            "name": "D200_DATA_1",
            "format": "yyyyddMM"
          },
          {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_HORA_1",
            "format": ""
          },
          {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_MOEDA_1",
            "format": ""
          },
          {
            "description": "Simula Double com contador",
            "type": "Double",
            "name": "D200_VALOR_1",
            "format": ""
          },
                    {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_TIPO_2",
            "format": ""
          },
          {
            "description": "Simula Date com contador",
            "type": "Date",
            "name": "D200_DATA_2",
            "format": "yyyyddMM"
          },
          {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_HORA_2",
            "format": ""
          },
          {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_MOEDA_2",
            "format": ""
          },
          {
            "description": "Simula Double com contador",
            "type": "Double",
            "name": "D200_VALOR_2",
            "format": ""
          }
        ]
      }
    ]
  },
  "dummyplugin_properties": {
  	"editavel": true,
  	"properties": {
  		"database": "mango_d", 
  		"usuario": "claudio", 
  		"senha": "mariposa"
  	}
  }
}
```

Agora vamos comentar os itens importantes desse layout e algumas convenções:

* A tag __class_name__ do layout é a classe do plugin com seu FQN (Fully Qualified Name), no primeiro nível do json
```
#!json
"class_name": "br.com.totvs.plugins.dummyplugin.DummyPlugin"
```

* Em seguida, acrescenta o nome do plugin, esse item identicará o seu plugin
```
#!json
"nome_acesso": "dummyplugin"
```

* Continuando, agora entra o _layout_ propriamente dito, onde contém as variáveis de entrada e saída; o nome dessa
tag deve __OBEDECER__ a um critério, sendo um prefixo __o nome do plugin__ mais o sufixo ___layout__; para nosso caso
essa tag será __dummyplugin_layout__, sendo esse sufixo usado internamente para a manipulação das variáveis.
```
#!json
"dummyplugin_layout": {
```

* Outra tag __OBRIGATÓRIA__ é a que indica se as propriedades daquela _tag_ são editáveis, na maioria das vezes essa _flag_
contem valor __true__, pois podemos aumentar o número de variáveis para um determinado registro, mas somente as variáveis 
de saída como veremos mas adiante.
```
#!json
"editavel": true
```

* Agora existem duas Listas contendo as variáveis, que __DEVEM TER__ as _tags_ com nome __entrada__ e __saida__, vamos 
falar separadamente dessas duas tags. A _tag_ __entrada__ contém todas as variáveis que serão enviadas ao plugin na sua
execução, que entra num Map() transformado internamente. Cada item da lista dessa _tag_ tem os detalhes de cada variável
e devem ter os nomes como abaixo, que informa o nome da variável, a descrição, o tipo de dado (um de: String, Date, Boolean,
Double e Integer) e um formato se for o caso, normalmente usado para datas:
```
#!json
    "entrada": [
        {
          "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": ""
        }
      ]

```

* Para o caso da _tag_ __saida__ (essa _tag_ é uma Lista) deve se obervar alguns detalhes a mais; essas serão as variáveis 
devolvidas (ou não) pelo plugin externo em questão. Os detalhes importantes estão nas _tags_ __id__ e __counter__ e __register__
que falaremos mais adiante.
```
#!json
    "saida": [
      {
        "id": "DUMMY",
        "counter": 0,
        "register": [
...
```
A _tag_ __id__ identifica o nome de um agrupamento de variáveis;

A _tag_ __counter__ identifica se o registro pode criar mais variáveis, mas __counter = 0__ indica que esse registro __NÃO__
vai ser "explodido" numa sequência, se _counter = 2_ então teremos a variável _var_ sendo "explodida" em var1, var2; obviamente
seu _plugin_ será capaz de também devolver essas sequências.

A _tag_ __register__ contém as variáveis em si, que veremos adiante.

Abaixo um _register_ de uma variável com _counter = 0_; observe que a _tag_ __register__ é igual ao conteúdo da _tag_ __entrada__,
mas isso não é muito importante, só curiosidade... internamente sim!
```
#!json
        "id": "DUMMY",
        "counter": 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": ""
          }
        ]
      }
```

Mas, e no caso de __counter > 0__? Isso indica que as variáveis de saída podem variar, podendo ter vários 
registros de uma única variável. Esse _counter_ se usado pelo __Editor__ ele "explode" essas variáveis 
concatenando o contador ao final da variável (costumo colocar "_" ao final da variável pra facilitar.
Entretanto você já pode informar diretamente o número de variáveis e as variáveis. Veja um _counter = 2_.
```
#!json
      {
        "id": "D200",
        "counter": 2,
        "register": [
          {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_TIPO_1",
            "format": ""
          },
          {
            "description": "Simula Date com contador",
            "type": "Date",
            "name": "D200_DATA_1",
            "format": "yyyyddMM"
          },
          {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_HORA_1",
            "format": ""
          },
          {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_MOEDA_1",
            "format": ""
          },
          {
            "description": "Simula Double com contador",
            "type": "Double",
            "name": "D200_VALOR_1",
            "format": ""
          },
                    {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_TIPO_2",
            "format": ""
          },
          {
            "description": "Simula Date com contador",
            "type": "Date",
            "name": "D200_DATA_2",
            "format": "yyyyddMM"
          },
          {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_HORA_2",
            "format": ""
          },
          {
            "description": "Simula String com contador",
            "type": "String",
            "name": "D200_MOEDA_2",
            "format": ""
          },
          {
            "description": "Simula Double com contador",
            "type": "Double",
            "name": "D200_VALOR_2",
            "format": ""
          }
        ]
      }

```

* Exemplo do plugin preenchendo essas variáveis
```
#!java
hashOut.put("D200_NUMCONSULTAS", 3D);
hashOut.put("D200_TIPO_1", "TIPO1");
hashOut.put("D200_DATA_1", (String) formatter.format(new Date()));
hashOut.put("D200_HORA_1", "21:59:59");
hashOut.put("D200_MOEDA_1", "R$");
hashOut.put("D200_VALOR_1", 100.01);
hashOut.put("D200_TIPO_2", config.get("senha"));
hashOut.put("D200_DATA_2", (String) formatter.format(new Date()));
hashOut.put("D200_HORA_2", "22:59:59");
hashOut.put("D200_MOEDA_2", "U$");
hashOut.put("D200_VALOR_2", 200.02);
hashOut.put("D200_TIPO_3", "TIPO3");
hashOut.put("D200_DATA_3", (String) formatter.format(new Date()));
hashOut.put("D200_HORA_3", "23:59:59");
hashOut.put("D200_MOEDA_3", "LB");
hashOut.put("D200_VALOR_3", 300.03);

```

### Colocando outros dados necessários no __layout__ ###

* Mas e seu você quiser incluir outras coisas nesse layout e depois recuperar no seu plugin?

Claro que pode! Novamente uma regra deve ser seguida para o nome dessas outras _tags_, como por
exemplo __SEMPRE__ ter o __sufixo__ "___properties__" que isso será devolvido uma _string_ com 
um JSON que pode ser remapeado como convier no seu _plugin_, abaixo vamos tentar explicar isso.
Veja um arquivo de propriedades incluído no _layout_.
```
#!json
 "dummyplugin_properties": {
  	"editavel": true,
  	"properties": {
  		"database": "mongo_d", 
  		"usuario": "claudio", 
  		"senha": "mariposa"
  	}
  }
```

A _tag_ __editavel__ segue a mesma regra, então o __Editor__ pode abrir essa _tag_ para se editada
A _tag_ __properties__ é uma coisa "livre", visto que ele devolve para o plugin uma _string json_.

No seu _plugin_ para ter acesso a essas _tags_ deve chamar a API abaixo e se preferir mapear, como
abaixo:
```
#!java
String jsonOut = PluginConfig.getDetails("dummyplugin");
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> config = mapper.readValue(jsonOut, new TypeReference<Map<String,Object>>(){});

```

### Montagem do arquivo de MANIFEST.MF ###

A montagem das informações sobre o _plugin_ devem ser informadas no arquivo de __MANIFEST.MF__ para
que seja possível fazer a importação e injeção do _.jar_ gerado no Intellector Server; basicamente colocando informações
no _pom.xml_ na _tag_ __todo o trabalho__ será feito automaticamente, como segue abaixo:
```
#!xml
	<properties>
		<!-- nome da package e classe - FQN -->
		<main-class>br.com.totvs.plugins.dummyplugin.DummyPlugin</main-class>
		<!-- Nome do Plugin de acesso -->
		<pluginname>dummyplugin</pluginname>
		<description>Dummy para exemplificar plugins externos</description>
		<!-- chave de acesso ao dado na persistencia -->
		<primarykey>CPF</primarykey>
		<pkdescription>CPF</pkdescription>
		<!-- Caminho para o Layout do Acesso -->
		<layoutpath>resources/dummyplugin.json</layoutpath>
	</properties>

...

			<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>
								</manifestEntries>
							</manifestSection>
						</manifestSections>
					</archive>
				</configuration>
			</plugin>

```

Que resultará num arquivo MANIFEST.MF conforme abaixo:
```
#!bash
Manifest-Version: 1.0
Built-By: ccardozo
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_131
Main-Class: br.com.totvs.plugins.dummyplugin.DummyPlugin

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

```

### Download de exemplo de Plugin Externo ###

* Para tudo ficar mais fácil, disponibilizamos o código fonte / projeto de um Plugin Externo, esse mesmo
que chamamos de __dummyplugin__ usado no tutorial acima:
https://bitbucket.org/intellectorserver/dummyplugin
