jump to navigation

Rhino: Executando código de javascript na plataforma java ! 27/03/2009

Posted by fredericobenevides in Rhino.
3 comments

Para quem não sabe, a jvm permite executar vários tipos de linguagens além da própria linguagem Java.

Alguns exemplos de outras linguagens que rodam na jvm:

  • JRuby (implementação em java da linguagem Ruby)
  • Groovy
  • Scala
  • JavaFX
  • Rhino (implementação Java do Javascript)
  • CajuScript (projeto brasileiro http://code.google.com/p/cajuscript/)

Alguém poderia perguntar o por que de usar outra linguagem na jvm além da própria linguagem java. Bem, um dos itens que eu diria, existem linguagens que oferecem mais agilidade para resolver algum problema do que a própria linguagem java. E pra quem mexe a muito tempo em java, e que conhece outras linguagens, percebe que a linguagem java não é tão bonita assim e nem tão ágil. Pra ser sincero, acredito mais no futuro da plataforma Java do que na linguagem, mas isso não vem ao caso nesse tópico.

Existem linguagens dinâmicas que oferecem mais agilidade para desenvolver e resolver vários problemas, uma delas, o Ruby. Por exemplo, para somar um array no Ruby 1.9 eu poderia simplesmente usar isso =>  %w[1 2 3 4 5].inject(:+)   Claro que pra quem não conhece a linguagem acha estranho, mas o detalhe é que em java teria mais algumas linhas. Então se achar que alguma linguagem te oferece mais produtividade em determinada parte do código, por que não usá-la?

Agora, o melhor de tudo é juntar a interoperabilidade que a jvm ofecere, e ainda ter linguagens mais práticas/ágéis podendo ser executadas em qualquer ambiente. Ótimo, não é mesmo!?

Vamos ao assunto que interessa. Rhino! O que tem de especial executar javascript na plataforma Java?  Já aconteceu de alguns projetos você ter que repetir códigos que são feito em validações de javascript usar no seu programa? Saco, não é mesmo!? Cadê a reutilização de código? Cadê a produtividade? (apesar que alguns casos vai no ctrl+c ctrl+v e mais adaptação do código).

Para resolver alguns destes problemas a gente pode utilizar o Rhino, e com isso termos reutilização de códigos. Desenvolvemos o código em javascript e pronto, fazemos o java chamar determinada função de javascript e voilà, seu código java já está funcionando sem ter necessidade que implementar tudo novamente. Detalhe por padrão no java 6 já vem o Rhino, facilitando a praticidade de não precisar baixar o rhino e colocar no classpath do seu projeto.

O processo para rodar o Rhino dentro do java.

  1. Criar a fábrica ScriptEngineManager para construir ScriptEngine
  2. Criar o objeto que vai manipular determinada linguagem
  3. Avaliar o conteúdo da linguagem e executá-la
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.eval("println('JavaScript Executando')");

Simples não é mesmo?

Agora vamos utilizar um arquivo de javascript, nesse próximo exemplo usando uma função fatorial.

Conteúdo do javascript:

function fatorial(n) {
	if (n == 1) return 1;
	return n * fatorial(n-1);
}
n = 3
println("O fatorial de " + n + " é: " + fatorial(n));

Código

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.eval(new FileReader("fatorial.js");

Se você reparou bem, quem está chamando a execucação da função está no próprio arquivo do javascript. “Mas não é isso que quero, quero passar o argumento pelo código java, tem jeito?”. Claro que tem, vamos ver:

Arquivo atualizado, sem a chamada para a função:

function fatorial(n) {
	if (n == 1) return 1;
	return n * fatorial(n-1);
}

Código:

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.eval(new FileReader("fatorial2.js");

Invocable invocable = (Invocable) engine;

int fatorial = 3;
Double resultado = (Double) invocable
    .invokeFunction("fatorial", fatorial );
System.out.println("O fatorial de " + fatorial + 
    " é: " + resultado.intValue());

Como podem ver, nesse código passei o meu engine para o tipo invocable. Especifiquei qual o nome da função, e passei o argumento para a função em que o resultado desta chamada me retorna um Object.

Que tal outro exemplo? Vamos fazer agora uma validação de e-mail. Olha, não estou entrando nada em detalhe de hibernate validator, de validação via ajax, mas sim no uso do Rhino dentro do Java, então não há de ter pensamentos: “eu poderia fazer isso usando hibernate validator”, “usando ajax”, etc. Lembre-se sempre disso, isso é para alguns casos que seu código de javascript é o mesmo do seu código java.

function validar_email(email) {
	regex = /^[a-zA-Z0-9][a-zA-Z0-9\._-]+@([a-zA-Z0-9\._-]+\.)[a-zA-Z-0-9]{2}/;
    if(regex.exec(email)) {
    	return true;
    } else {
    	return false;
    }
}

Código:

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.eval(FileLoad.load("email.js"));

Invocable invocable = (Invocable) engine;

String email = "usuario@email.com.br";
Boolean resultado = (Boolean) invocable
    .invokeFunction("validar_email", email);
System.out.println("O email é válido ? " + resultado);

email = "usuario";
resultado = (Boolean) invocable
    .invokeFunction("validar_email", email);
System.out.println("O email é válido ? " + resultado);

Interessante não? Mas você sabia também que é possível utilizar as bibliotecas do java escrevendo com javascript? E que tal implementar um JFrame escrevendo javascript? Ah, e que tal não ter que preocupar em declarar o tipo de objeto, já que javascript é uma linguagem dinâmica? Pois é, o Rhino permite fazer tudo isso!

Vejamos um exemplo como seria criar uma janela:

Arquivo js:

importPackage(javax.swing);
importPackage(java.awt);

var frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 400);
frame.setVisible(true);

Como podem ver para importar usando o javascript preciso digitar, importPackage(nomeDoPacote)  , o resto é normal como se fosse em java, mas claro, sem ter a necessidade de declarar o tipo do objeto, afinal em javascript não precisa.

Código

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.eval(new FileReader("simples_jframe.js");

Para rodar, simplesmente carrega o arquivo, e pronto, ele mostrará o JFrame.

Achei até interessante isso de desenvolver java usando javascript, que eu fiz um estudo de implementar um JFrame usando Threads. Caso vocês tenham interesse, vocês podem baixar o projeto “blog_rhino” no seguinte endereço do github: git://github.com/fredericobenevides/blog_rhino.git. Dentro desse projeto também terá todos os exemplos citados aqui no blog.

Passos para quem não sabe baixar o projeto e importar para o eclipse:

  1. Digitar o seguinte comando no console: git clone   git://github.com/fredericobenevides/blog_rhino.git
  2. cd blog_rhino
  3. mvn eclipse:eclipse (Fazendo o maven criar os arquivos necessários para importar para o eclipse)
  4. Abrir o eclipse e acessar File -> import -> General -> Existing Projects Into Workspace
  5. “Brincar com o projeto”

Espero que tenham curtido, e se caso um dia achar que possa reutilizar um código javascript… por que não usar Rhino?