Executar JRuby a partir do Java

Nos últimos tempos tenho dedicado boa parte do meu tempo livre estudando JRuby. Tem sido uma verdadeira diversão!

Com o objetivo de compartilhar um pouco do que tenho aprendido, eis aqui este post…

Quer dizer que o osrevni inverso também é verdade?

Muito se fala da capacidade do JRuby de acessar código Java de maneira tão natural quanto o faz com seu próprio código – o que é definitivamente fantástico. Mas não tenho visto muitos exemplos de código Java acessando código JRuby. Por quê? Não sei dizer. Talvez porque não tenham visto tanta utilidade nisso. Essa não é minha opinião, já que tenho interesse em implementar algumas coisas em JRuby e usar a partir do Java.

Uma das coisas que fiz nesses meus estudos sobre usar JRuby a partir de Java foi testar o compilador jrubyc para gerar .class, mas não cheguei bem onde eu queria – até troque umas palavras com Charles Nutter a respeito -, porque o .class gerado por ele não é do tipo que se pode instanciar e usar diretamente num código Java, dada a natureza totalmente dinâmica de Ruby. Nas palavras do próprio Charles:

The code compiled by jrubyc is not a “normal” Java class[...] This is not a Java class you can instantiate and call methods on directly from Java[...].

Como não desisti, tenho algumas alternativas para compartilhar.

OBS.: Para executar os exemplos apresentados é necessário ter jruby.jar no classpath do seu projeto. Quando escrevi este post estava usando a versão 1.0, porque não havia uma versão mais atual na máquina que eu estava usando. Mas agora já atualizei o código para a versão 1.1.3.

Primeira alternativa: JRuby puramente Ruby

No exemplo abaixo, criou uma classe Ruby comum – sem qualquer recurso específico do JRuby – e, logo após, a carrego, instancio e executo seu método a partir do Java.

matematica_apenas_ruby.rb

class MatematicaApenasRuby
  def soma(a, b)
    a + b
  end
end

MatematicaApenasRubyTest.java

public class MatematicaApenasRubyTest {
    public static void main(String args[]) throws Exception {
        List pathsLoad = new ArrayList();
        pathsLoad.add("/Workspace/Ruby/IntegracaoJava/lib/");

        Ruby rubyRuntime = JavaEmbedUtils.initialize(pathsLoad);
        rubyRuntime.getLoadService().load("matematica_apenas_ruby.rb", false);

        Object mat_ruby = rubyRuntime.evalScriptlet("MatematicaApenasRuby.new");
        Integer res_ruby = (Integer)JavaEmbedUtils.invokeMethod(rubyRuntime, mat_ruby, "soma", new Integer[] {3, 2}, Integer.class);

        System.out.println("Soma 3 + 2 invocando diretamente JRuby: " + res_ruby);
    }
}

O que esse código Java faz é bem simples, ele:

1- Inicializa um ambiente de runtime para Ruby, indicando onde estão os arquivos .rb;
2- Executa o script de criação da classe MatematicaApenasRuby;
3- Executa o script de instanciação da classe MatematicaApenasRuby;
4- E, por fim, invoca o método soma passando dois parametros.

Que tal, acho simples? Pois muito bem, continuemos…

Segunda alternativa: JRuby implementando Java

Neste segundo exemplo, o que eu faço é criar uma classe JRuby que estende uma classes Java abstrata, o que facilita ainda mais na hora de usar a partir do Java. Vejamos como fica.

matematica_impl_java.rb

require 'java'

class MatematicaImplJava < Java::IntegracaoPoliglota::Matematica
  def soma(a, b)
    a + b
  end
end

Matematica.java

package integracao.poliglota;

public abstract class Matematica {
    public abstract int soma(int a, int b);
}

MatematicaImplJavaTest.java

public class MatematicaImplJavaTest {
    public static void main(String args[]) throws Exception {
        List pathsLoad = new ArrayList();
        pathsLoad.add("/Workspace/Ruby/IntegracaoJava/lib/");

        Ruby rubyRuntime = JavaEmbedUtils.initialize(pathsLoad);
        rubyRuntime.getLoadService().load("matematica_impl_java.rb", false);

        Object mat_impl_java = rubyRuntime.evalScriptlet("MatematicaImplJava.new");
        Matematica matematica = (Matematica)JavaEmbedUtils.rubyToJava(rubyRuntime, (IRubyObject)mat_impl_java, Matematica.class);

        System.out.println("Soma 10 + 2 usando a interface Java: " + matematica.soma(10, 2));
    }
}

E esta alternativa, gostou? Eu gostei bastante. Porque no meu caso, o que eu quero é poder definir uma interface em Java e implementar com JRuby a la Ruby Way e depois usar no Java. É claro que não estou levando em conta o fator “performance”, só estou considerando o fator “alternativa de implemententação”. Só isso.

Mas vamos lá, o que esse código faz?

1- A classe JRuby estende uma classe Java abstrata, como dito antes;
2- A classe que faz o teste, em linhas gerais, faz um cast do objeto JRuby para a classe Java abstrata;
3- E no final das contas, invoca o método da classe Java.

Será que programação poliglota é o futuro?

Se é ou não é, eu não sei. Mas sei que deixei de ser um arquiteto de uma nota só há muito tempo; e estou muito emplogado com JRuby – ele é o melhor dos dois mundos!

E você, o que acha? Deixe um comentário…


14 Responses to “Executar JRuby a partir do Java”

Leave a Reply