5 coisas que você não sabia sobre ... a API Java Scripting

Uma maneira mais fácil de criar scripts na plataforma Java

A linguagem Java™ é mais do que você precisará para alguns projetos, mas linguagens de scripts são famosas pela deficiência em desempenho. Descubra como a API de script do Java (javax.script) entrega o melhor de dois mundos, permitindo que você chame scripts a partir de seus programas Java e vice versa.

Ted Neward, Principal, Neward & Associates

Ted Neward photoTed Neward é o diretor da Neward & Associates, onde ele dá consultoria, orientação, ensina e faz apresentações sobre Java, .NET, Serviços XML e outras plataformas. Ele reside perto de Seattle, Washington.



07/Out/2011

Sobre esta série

Então você acha que conhece programação em Java? O fato é que a maior parte dos desenvolvedores tem simplesmente um conhecimento superficial da plataforma Java e sabe apenas o suficiente para fazer seu trabalho. Nesta série, Ted Neward aprofunda a discussão sobre a funcionalidade essencial da plataforma Java para revelar dados pouco conhecidos que podem ajudar a solucionar até mesmo os mais complexos desafios de programação.

Atualmente, muitos desenvolvedores Java estão interessados em usar linguagem de script na plataforma Java, mas usar uma linguagem dinâmica que foi compilada no bytecode do Java nem sempre é possível. Em alguns casos, é mais rápido e eficiente simplesmente fazer scripts de partes de um aplicativo Java ou chamar os objetos Java particulares necessários de dentro de um script.

É onde entra o javax.script. A API de script do Java, introduzida no Java 6, acaba com a brecha entre pequenas linguagens úteis de script e o ecossistema robusto do Java. Com a API de script do Java, é possível rapidamente integral virtualmente qualquer linguagem de script com seu código Java, o que abre suas opções de forma considerável ao resolver pequenos problemas em domínios de outra pessoa.

1. Executando o JavaScript com jrunscript

Cada novo release da plataforma Java traz consigo um novo conjunto de ferramentas de linha de comando dentro do diretório bin do JDK. O Java 6 não foi exceção e o jrunscript não é uma adição pequena aos utilitários da plataforma Java.

Considere o problema básico de escrever um script de linha de comando para monitoração de desempenho. A ferramenta pegará emprestado o jmap (apresentado no artigo anterior da série) e o executará a cada 5 segundos com relação a um processo Java, de forma a obter uma ideia de como o processo está sendo executado. Normalmente, scripts de shell de linha de comandos servem, mas, neste caso, o aplicativo do servidor foi implantado em uma variedade de plataformas diferentes, incluindo Windows® e Linux®. Os administradores de sistema testemunharão que escrever scripts de shell que funcionem em ambas as plataformas é problemático. A solução normal é escrever um arquivo de lote do Windows e um script de shell do UNIX® e simplesmente manter os dois em sincronização no decorrer do tempo.

Mas, como qualquer leitor do The Pragmatic Programmer sabe, esta é uma grave violação do princípio DRY (don't repeat yourself - não se repita) e é um local propício para erros e defeitos. O que gostaríamos de fazer é escrever algum tipo de script independente do sistema operacional que possa ser executado em todas as plataformas.

A linguagem Java independe de plataforma, é claro, mas não é realmente o caso para uma linguagem de "sistema". O que precisamos é de uma linguagem de script — como o JavaScript, por exemplo.

A Listagem 1 inicia com um shell básico do que queremos:

Listagem 1. periodic.js
while (true) {     echo("Hello, world!"); }

Muitos, se não todos, desenvolvedores Java já conhecem JavaScript (ou ECMAScript; JavaScript é um dialeto do ECMAScript de propriedade da Netscape) graças às nossas interações forçadas com navegadores da web. A pergunta é, como um administrador de sistemas executaria este script?

A solução, é claro, é o utilitário jrunscript que vem com o JDK, como mostra a Listagem 2:

Listagem 2. jrunscript
C:\developerWorks\5things-scripting\code\jssrc>jrunscript periodic.js
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
...

Note que você também poderá usar um ciclo for para executar o script um certo número de vezes antes de sair. Basicamente, o jrunscript permite que você faça quase tudo o que normalmente faria com o JavaScript. A única exceção é que o ambiente não é um navegador, portanto não há DOM. As funções e objetos de nível superior disponíveis são, portanto, ligeiramente diferentes.

Como o Java 6 vem com o mecanismo Rhino ECMAScript como parte do JDK, o jrunscript pode executar qualquer código ECMAScript alimentado a ele, seja de um arquivo (como mostrado aqui) ou em um ambiente shell mais interativo chamado REPL ("Read-Evaluate-Print-Loop"). Simplesmente execute o jrunscript por si só para acessar o shell do REPL.


2. Acessando objetos Java a partir de um script

Ser capaz de escrever código em JavaScript/ECMAScript é bom, mas não queremos ter que reconstruir tudo o que usarmos na linguagem Java do zero — não é esta a finalidade. Felizmente, qualquer coisa que use os mecanismos da API de script do Java tem acesso completo ao ecossistema do Java pois, por dentro, tudo ainda é bytecode do Java. Portanto, voltando ao problema anterior, poderemos iniciar processos a partir da plataforma Java usando a chamada tradicional Runtime.exec(), como mostra a Listagem 3:

Listagem 3. Runtime.exec() executa jmap
var p = java.lang.Runtime.getRuntime().exec("jmap", [ "-histo", arguments[0] ])
p.waitFor()

A array arguments é a referência integrada padrão do ECMAScript aos argumentos passados a esta função. No caso do ambiente de script de nível superior, esta é a array de argumentos passada para o script em si (os parâmetros de linha de comando). Portanto, na Listagem 3, o script espera um argumento contendo o VMID do processo Java a mapear.

De forma alternativa, poderíamos tirar vantagem do fato de que jmap é uma classe Java e só chamar seu método main(), como na Listagem 4. Esta abordagem elimina a necessidade de "encadear" os fluxos in/out/err do objeto Process.

Listagem 4. JMap.main()
var args = [ "-histo", arguments[0] ] Packages.sun.tools.jmap.JMap.main(args)

A sintaxe Packages é uma notação do Rhino ECMAScript usada para referenciar um pacote Java fora dos pacotes principais java.* já configurados com o Rhino.


3. Chamando em scripts a partir do código Java

Chamando objetos Java a partir de um script é somente metade da história: O ambiente de script do Java também oferece a capacidade de invocar scripts de dentro do código Java. Fazer isto só requer instanciar uma ScriptEngine e, a seguir, carregar e avaliar o script, como mostra a Listagem 5:

Listagem 5. Script na plataforma Java
import java.io.*;
import javax.script.*;

public class App
{
    public static void main(String[] args)
    {
        try
        {
            ScriptEngine engine = 
                new ScriptEngineManager().getEngineByName("javascript");
            for (String arg : args)
            {
                FileReader fr = new FileReader(arg);
                engine.eval(fr);
            }
        }
        catch(IOException ioEx)
        {
            ioEx.printStackTrace();
        }
        catch(ScriptException scrEx)
        {
            scrEx.printStackTrace();
        }
    }
}

O método eval() também pode operar com relação a um String direto, de forma que o script não precisa vir de arquivos no sistema de arquivos — pode vir de um banco de dados, do usuário ou até mesmo ser construído dentro do aplicativo com base nas circunstâncias e na ação do usuário.


4. Vinculando objetos Java no espaço do script

Simplesmente invocar um script não é suficiente: Os scripts frequentemente querem interagir com objetos criados dentro do ambiente Java. Neste caso, o ambiente host do Java deverá criar objetos e vinculá-los, para que seja fácil para o script encontrá-los e usá-los. Esta é uma tarefa para o objeto ScriptContext mostrada na Listagem 6:

Listagem 6. Vinculando objetos para scripts
import java.io.*;
import javax.script.*;

public class App
{
    public static void main(String[] args)
    {
        try
        {
            ScriptEngine engine = 
                new ScriptEngineManager().getEngineByName("javascript");
                
            for (String arg : args)
            {
                Bindings bindings = new SimpleBindings();
                bindings.put("author", new Person("Ted", "Neward", 39));
                bindings.put("title", "5 Things You Didn't Know");
                
                FileReader fr = new FileReader(arg);
                engine.eval(fr, bindings);
            }
        }
        catch(IOException ioEx)
        {
            ioEx.printStackTrace();
        }
        catch(ScriptException scrEx)
        {
            scrEx.printStackTrace();
        }
    }
}

Acessar o objeto vinculado é direto — o nome do objeto vinculado é apresentado no nível do script como um membro do espaço de nome global, de forma que usar o objeto Person a partir do Rhino é tão fácil quanto o que mostra a Listagem 7:

Listagem 7. Quem escreveu este artigo?
println("Hello from inside scripting!")

println("author.firstName = " + author.firstName)

Como pode ser visto, propriedades no estilo JavaBeans são reduzidas a acessos diretos de nome, como se fossem campos.


5. Compilando scripts usados frequentemente

A desvantagem das linguagens de script sempre foi o desempenho. O motivo é que, na maioria dos casos, a linguagem de script é interpretada "no momento" e perde tempo e ciclos de CPU, tendo que analisar e validar texto à medida que é executada. Muitas linguagens de script em execução na JVM no final transformam o código de entrada em bytecode do Java, pelo menos na primeira vez que o script é analisado e validado; esta compilação dinâmica é descartada quando o programa Java é desligado. Manter scripts frequentemente usados em formato de bytecode daria uma significativa melhoria no desempenho.

Podemos fazer isto de uma forma natural e significativa com a API de script do Java. Se o ScriptEngine retornado implementar a interface Compilable, então o método de compilação naquela interface poderá ser usado para compilar o script (passado como String ou como Reader) em uma instância CompiledScript, que poderá, a seguir, ser usada para eval() o código compilado várias vezes, com diferentes vinculações. Isso é mostrado na Listagem 8.

Listagem 8. Compilando código interpretado
import java.io.*;
import javax.script.*;

public class App
{
    public static void main(String[] args)
    {
        try
        {
            ScriptEngine engine = 
                new ScriptEngineManager().getEngineByName("javascript");
                
            for (String arg : args)
            {
                Bindings bindings = new SimpleBindings();
                bindings.put("author", new Person("Ted", "Neward", 39));
                bindings.put("title", "5 Things You Didn't Know");
                
                FileReader fr = new FileReader(arg);
                if (engine instanceof Compilable)
                {
                    System.out.println("Compiling....");
                    Compilable compEngine = (Compilable)engine;
                    CompiledScript cs = compEngine.compile(fr);
                    cs.eval(bindings);
                }
                else
                    engine.eval(fr, bindings);
            }
        }
        catch(IOException ioEx)
        {
            ioEx.printStackTrace();
        }
        catch(ScriptException scrEx)
        {
            scrEx.printStackTrace();
        }
    }
}

Na maioria dos casos, a instância CompiledScript deverá ser mantida em algum lugar do armazenamento de longo prazo (servlet-context, por exemplo) de forma a evitar a recompilação do mesmo script repetidamente. No entanto, se o conteúdo do script mudar, será preciso criar um novo CompiledScript para refletir a mudança; uma vez compilado, o CompiledScript não mais executará o conteúdo do arquivo original do script.


Conclusão

A API de script do Java é um enorme passo na direção de estender o alcance e a funcionalidade de programas em Java e traz ganhos de produtividade associados com linguagens de script para dentro do ambiente Java. Acoplado com o jrunscript— que, obviamente, não é um programa completo de escrever —javax.script dá aos desenvolvedores Java os benefícios de linguagens de script, como Ruby (JRuby) e ECMAScript (Rhino), sem precisar render-se ao ecossistema e à escalabilidade do ambiente Java.

A seguir, na série 5 coisas: JDBC.

Recursos

Aprender

Discutir

  • Envolva-se na comunidade My developerWorks. Entre em contato com outros usuários do developerWorks enquanto explora os blogs, fóruns, grupos e wikis dos desenvolvedores.

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java
ArticleID=507683
ArticleTitle=5 coisas que você não sabia sobre ... a API Java Scripting
publish-date=10072011