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…

13 Respostas para “Executar JRuby a partir do Java

  1. Muito legal !
    Valeu pelo “pionerismo”, realmente não tinha visto o JRuby por esta perspectiva.

  2. Sem dúvida, ficar numa linguagem só é muito monótono.

  3. 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?😀

  4. @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… =)

  5. 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🙂

  6. É, realmente não é lá muito intuitivo mesmo…🙂

    Opa! Que legal! Contaêêê…

  7. Pingback: Interpretando Ruby e outras linguagens de script dentro da plataforma Java

  8. Leandro poderia dizer se o que está sendo adicionado ao método add são as libs???

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

  10. Neste caso qual seria a diferença entre eu add as libs no claspath ou add através do método add?

    Grato!

  11. 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? : /

  12. 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!

  13. Leandro minha monografia fala desse assunto de interoperabilidade.
    Será que poderiamos bater um papo via gtalk ou email mesmo?

    caso possa ai segue meu email: anderson.bonavides@gmail.com

Deixe uma resposta

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 )

Imagem do Twitter

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

Foto do Facebook

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

Foto do Google+

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

Conectando a %s