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…

August 14th, 2008 at 1:44 pm
Muito legal !
Valeu pelo “pionerismo”, realmente não tinha visto o JRuby por esta perspectiva.
August 14th, 2008 at 1:50 pm
Sem dúvida, ficar numa linguagem só é muito monótono.
August 15th, 2008 at 1:21 am
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?
August 15th, 2008 at 12:35 pm
@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… =)
August 24th, 2008 at 11:17 pm
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
August 25th, 2008 at 11:22 am
É, realmente não é lá muito intuitivo mesmo…
Opa! Que legal! Contaêêê…
August 29th, 2008 at 3:02 am
[...] o BSF (Bean Script Framework), do Apache. Outro jeito é utilizando o JRuby puro, como foi mostrado aqui. O terceiro jeito, e do qual eu gostei mais, é utilizando a implementação JRuby para à [...]
September 10th, 2008 at 12:31 am
Leandro poderia dizer se o que está sendo adicionado ao método add são as libs???
September 10th, 2008 at 1:39 pm
Sim, é adicionado o diretório onde contém os arquivos .rb que poderam ser carregado pelo método load, da classe LoadService.
September 10th, 2008 at 10:30 pm
Neste caso qual seria a diferença entre eu add as libs no claspath ou add através do método add?
Grato!
September 10th, 2008 at 10:50 pm
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? : /
September 13th, 2008 at 12:53 am
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!
September 13th, 2008 at 12:55 am
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
June 8th, 2010 at 6:49 pm
[...] http://leandrosilva.com.br/2008/08/14/executar-jruby-a-partir-do-java/ http://kenai.com/projects/jruby/pages/JRubyCompiler http://www.mouseoverstudio.com/blog/2008/08/29/interpretando-ruby-e-outras-linguagens-de-script-dentro-da-plataforma-java/ [...]