Integração com Java
O Engine permite a integração com Java por meio da API Enginelet. Essa API permite a execução síncrona de códigos Java a partir do JavaScript e vice-versa. Os principais componentes dessa API são:
- Classe JavaScript
Enginelet
: permite a execução de classes Java que implementem a interface “com.nginstack.engine.Enginelet”. - Interface Java
com.nginstack.engine.Enginelet
: define o métodohandleCommand
que deverá ser implementado pelas classes Java que serão executadas a partir do ambiente JavaScript. - Classe Java
com.nginstack.engine.EngineJavaInterface
: define métodos estáticos que permitem que o código Java execute um script JavaScript no Engine.
As classes Enginelet
e EngineJavaInterface
criam uma interface padronizada de comunicação entre
os dois ambientes utilizando parâmetros e retornos do tipo String
. Parâmetros de outros tipos
devem ser convertidos em strings, sendo recomendado o uso do JSON para tipos mais complexos.
Os erros lançados durante a execução do script JavaScript indicado no método runScript
da classe
EngineJavaInterface
são capturados pelo Engine e relançados dentro do ambiente Java. O
mesmo ocorre com os erros Java lançados durante a execução do método runCommand
da classe
Enginelet
. Esses erros devem ser esperados pelo desenvolvedor e devem ser tratados adequadamente.
Atualmente não é possível depurar a execução de códigos Java executados a partir do Engine. Para auxiliar na investigação dos erros, a saída padrão e a de erro do Java são interceptadas pelo Engine e redirecionadas para o arquivo “java.log”, permitindo que elas possam ser utilizadas para coletar informações relevantes durante a execução dos códigos Java.
Importante: o Java é integrado ao Engine via biblioteca nativa da JVM. Essa integração cria
um acoplamento forte entre o Engine e o Java em um mesmo processo do sistema operacional, tornando
impossível isolar a alocação de recursos e falhas de cada ambiente. A memória alocada pelo ambiente
Java se mistura com a memória alocada pelo Engine e eventuais falhas de códigos Java podem
comprometer a estabilidade e segurança do Engine, podendo inclusive provocar a queda do serviço.
Por esse motivo, é recomendado que bibliotecas Java de alta complexidade, que aloquem muitos
recursos ou que sejam implementadas por terceiros não sejam integradas via Enginelet. Para esses
casos, pode-se utilizar a classe OSApplication
para disparar uma aplicação Java em um processo
separado e se integrar com ela por meio de uma API HTTP, Web Sockets ou arquivos.
Pré-requisitos
A API Enginelet é compatível com o Java 8 ou superior. O Engine detecta automaticamente o runtime Java a partir das configurações do registro do Windows ou do PATH. O caminho de uma instalação específica do Java pode ser informado ao Engine caso seja necessário, conforme orientação do manual Instalação do sistema.
Configuração
As classes e interfaces Java de integração do sistema são distribuídas no arquivo “enginelet.jar”. Esse arquivo é armazenado no diretório “/products/Engine/library/javalibs/” da Virtual File System. Para permitir a compilação das classes Java integradas ao sistema, é necessário fazer o download desse arquivo e adicioná-lo ao CLASSPATH do Java utilizado no ambiente de desenvolvimento. Para simplificar esse processo, esse pacote também é distribuído em um repositório Maven que pode ser configurado em uma ferramenta de build automatizada.
O pacote “enginelet” não é distribuído no Maven Central Repository, portanto, além da dependência em si, o repositório da plataforma também deve ser configurado na ferramenta de build utilizada. Essa configuração precisa ser realizada uma única vez, adicionando as configurações abaixo nos locais relevantes:
Gradle (build.gradle):
plugins {
id 'com.google.cloud.artifactregistry.gradle-plugin' version '2.2.1'
}
repositories {
maven {
url "artifactregistry://us-east1-maven.pkg.dev/nginstack/java"
}
}
Maven (pom.xml):
<project>
<!-- Other configs -->
<repositories>
<!-- Other repositories -->
<repository>
<id>artifact-registry</id>
<url>artifactregistry://us-east1-maven.pkg.dev/nginstack/java</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<build>
<!-- Other build configs -->
<extensions>
<extension>
<groupId>com.google.cloud.artifactregistry</groupId>
<artifactId>artifactregistry-maven-wagon</artifactId>
<version>2.2.0</version>
</extension>
</extensions>
</build>
</project>
Uma vez configurado o repositório da plataforma, deve ser adicionada a dependência “enginelet”. A versão desse pacote segue uma numeração diferente do restante do sistema. Para determinar a versão adequada, deve ser observado o histórico de alterações no final deste manual e ser escolhida uma versão compatível com a versão do sistema na qual a classe Java será utilizada. É importante observar que, independente da versão escolhida, a versão do “enginelet” que de fato será utilizada é aquela do pacote JAR gravado na Virtual File System, normalmente a última versão disponível.
Seguem abaixo as configurações que devem ser realizadas no Gradle ou Maven para utilizar a última versão do pacote:
Gradle (build.gradle):
implementation group: 'com.nginstack', name: 'enginelet', version: '1.0'
Maven (pom.xml):
<dependency>
<groupId>com.nginstack</groupId>
<artifactId>enginelet</artifactId>
<version>1.0</version>
</dependency>
Caso o VSCode seja utilizado no desenvolvimento, é recomendado que as duas configurações abaixo
sejam adicionadas no arquivos .vscode/settings.json
, permitindo que os fontes e o JavaDoc do
Enginelet sejam descarregados na instalação da dependência:
"java.eclipse.downloadSources": true,
"java.maven.downloadSources": true,
Distribuição
Os códigos Java relacionados às integrações com o sistema devem ser compilados e distribuídos por meio de pacotes JAR. Esses pacotes JAR devem ser gravados na Virtual File System, em um diretório com o nome “javalibs”.
Por convenção, cada produto do sistema tem o seu próprio diretório “javalibs”, normalmente no caminho “/products/<product-name>/library/javalibs”. É recomendado que pacotes relacionados às customizações sejam gravados no caminho “/products/custom/library/javalibs”.
No primeiro uso da API Enginelet, o Engine grava todos os arquivos dos diretórios “javalibs” da Virtual File System no diretório local do sistema operacional “<engine.dataDir>/javalibs”. Em seguida ele adiciona todos os arquivos desse diretório no CLASSPATH do ambiente Java. Esse sincronismo é realizado uma única vez antes da inicialização da Java VM, portanto as alterações nos arquivos dos diretórios “javalibs” exigem o reinício do Engine para serem efetivadas.
O sincronismo automático das dependências Java pode ser desativado caso seja necessário testar uma modificação sem alterar os arquivos da Virtual File System. Para isso deve ser criado o arquivo “<engine.dataDir>/javalibs/.ignoresync” e os JAR atualizados devem ser gravados diretamente no diretório “javalibs” do sistema operacional.
Exemplos de uso
Implementação de uma classe Java Enginelet
package com.example;
import com.nginstack.engine.Enginelet;
import com.nginstack.engine.UnsupportedCommandException;
public class EchoEnginelet implements Enginelet {
public String handleCommand(String commandName, String[] commandArgs) {
if (commandName.equals("echo")) {
return String.join(",", commandArgs);
} else {
throw new UnsupportedCommandException(commandName);
}
}
}
Utilização de uma classe Java Enginelet no JavaScript
const Enginelet = require('@nginstack/engine/lib/java/Enginelet.js');
const enginelet = new Enginelet('com.example.EchoEnginelet');
enginelet.handleCommand('echo', 'test'); // => 'test'
Execução de um script JavaScript a partir do Java
package com.example;
import java.util.HashMap;
import com.nginstack.engine.EngineJavaInterface;
import com.nginstack.engine.Enginelet;
public class RunScriptEnginelet implements Enginelet {
private static long scriptKey = 123456789;
public String handleCommand(String commandName, String[] commandArgs) {
HashMap<String, String> parameters = new HashMap<>();
parameters.put("value", "test");
if (commandName.equals("test")) {
return EngineJavaInterface.runScript(scriptKey, parameters);
} else {
throw new UnsupportedCommandException(commandName);
}
}
}
Leitura dos parâmetros enviados a partir do Java
const result = 'Value: ' + javaRequest.getParameter('value');
result; // => 'Value: test'
Histórico de alterações
- Versão 1.0: versão inicial do pacote no repositório Maven. Compatível com o Engine 71 ou superior.