Executar JRuby a partir do Java

[Novo endereço: leandrosilva.com.br.]

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
&#91;/sourcecode&#93;

<em>Matematica.java</em>


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…

Autor: Leandro Silva

I do code for a happy living.

14 comentários em “Executar JRuby a partir do Java”

  1. Muito legal o artigo, mas acho que a sintaxe poderia melhorar bem mais, a achei um pouco confusa devido a que não consegui identificar o que fazia a simples vista. E uma oportunidade para meter a mão ne? 😀

  2. @Diego
    Que código você achou que a sintax não tá legal?

    A alternativa 2, como eu disse, eu acho mais interessante. E pra melhorar o legibilidade também fica bem fácil, basta criar uma factory que encapsule o “cast” da classe JRuby para a classe Java estendida e boa.

    De qualquer forma, estou pesquisando e vendo as alternativas possíveis. Quem sabe não chego em algo melhor… =)

  3. Oi Leandro, me referia a algumas linhas da API do JRuby, como por exemplo:

    (Integer)JavaEmbedUtils.invokeMethod(rubyRuntime, mat_ruby, “soma”, new Integer[] {3, 2}, Integer.class);

    O problema é encontrar outro jeito de fazer né, certeza chamar Ruby pelo Java e mais difícil que o contrario.

    Ah, teu post ta me servindo bastante num negocio que estou fazendo, já te conto 🙂

  4. Sim, é adicionado o diretório onde contém os arquivos .rb que poderam ser carregado pelo método load, da classe LoadService. 🙂

  5. Quando você inicializa um runtime, você indica onde ele deve procurar as libs (.rb) que você tentará carregar com o load service. Assim, cada runtime que você inicializar, terá seu próprios paths pra localizar suas libs.

    Clariei ou compliquei? : /

  6. Clareou sim. =)
    Na verdade o que eu estava fazendo era colocar a minha pasta lib(jar’s): pathsLoad.add(“../Monografia/ruby/lib/”);

    Em seguida eu fazia: getLoadService().load(“../Monografia/ruby/MatematicaApenasRuby.rb”, false);

    😦

    Mas saquei o que vc falou e corrigi o para:
    pathsLoad.add(“../Monografia/ruby”);

    getLoadService().load(“MatematicaApenasRuby.rb”, false);

    Obrigado!

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s