Instantiating and using a JRuby object from Java


#1

JRuby will be used inside an existing Java framework. I have a JRuby
class like this:

class JRController
def initialize(x)

end
def execute

end
end

From my Java framework, I need to create a JRController object (passing
a native Java object to it), and then invoke its execute method.

Can this be done, and if yes, how would the Java code look like? I
searched on the Net for examples, where the main program is written in
Java, and JRuby is used within it, but found only solutions where a
JRuby script is executed from the Java side, which does not apply to my
case.

Any help appreciated…


#2

Ronald,

I am assuming that your main application is a Ruby app and you are
integrating it with Java. I assume this since it appears you are not
interested in having a ScriptingContainer execute a ruby script.

Is this what you want?

Suppose I have a java class:

public class Silly {

public void go(Runnable r) {
r.run();

}

I want my java class to call into the land of Ruby:

class Foo
include java.lang.Runnable
def run
puts “Hi From Ruby”
end
end

the Ruby code:

s.go(Foo.new) yields “Hi From Ruby” class as expected

OK now suppose you have the following java interface:

public interface Build {
Runnable build();
}

And, to the Silly class, I add:

public void buildAndGo(Build b)
{
Runnable r = (Runnable)b.build();
go®;
}

in the land of Ruby we add:

class Faa
def build
Foo.new
end
end

If we have:

s = Silly.new

then we can:

irb(main):018:0> s.buildAndGo(Faa.new)
Hi From Ruby

So java built up a Foo, via Faa, and used Foo’s run method.

Cris

Ronald F. wrote in post #1179303:

JRuby will be used inside an existing Java framework. I have a JRuby
class like this:

class JRController
def initialize(x)

end
def execute

end
end

From my Java framework, I need to create a JRController object (passing
a native Java object to it), and then invoke its execute method.

Can this be done, and if yes, how would the Java code look like? I
searched on the Net for examples, where the main program is written in
Java, and JRuby is used within it, but found only solutions where a
JRuby script is executed from the Java side, which does not apply to my
case.

Any help appreciated…


#3

Cris S. wrote in post #1179316:

Ronald,

I am assuming that your main application is a Ruby app and you are
integrating it with Java. I assume this since it appears you are not
interested in having a ScriptingContainer execute a ruby script.

Actually no. The ‘main’ is in Java (it is an already existing Java
application), and new features should be added. Since the interface
between the main application and the new features is pretty small -
basically, a few Java objects need to be created and passed to the new
code, and another Java object is being returned - I was thinking to use
JRuby to implement the new features, instead of writing everything in
Java. The main program can’t be made into Java, since we can’t touch
this part.

In order to invoke my Ruby code, my Java application needs to somehow
execute a Ruby method and pass the objects. This means that the Java
code also needs to know the class, where this method belongs to.

Let me sketch it in detail. I’m using in this example class methods
instead of instance methods, because in hindsight, the seem to be better
suited for my problem. I have a Ruby class like this:

class MyRubyClass
def self.doit(x)

end
end

On the Java Side, this I need to invoke this method:

MyJavaDataClass input_data = MyJavaDataClass.new;
my_java_data.loadData;
MyJavaDataResponse output_data MyRubyClass.doit(my_java_data)

Of course, this doesn’t work: Java doesn’t know anything about the class
MyRubyClass or the method ‘doit’. How would I write the “glue” between
the Java and the Ruby world?

Ronald


#4

Also…

I could do this:

irb(main):020:0* s.buildAndGo(->{Foo.new})
Hi From Ruby


#5

OK…

How is this:

We have the following ruby script (foo.rb):

puts “Hi world from the land of Ruby!”

java_import ‘foo.DoIt’ do |p, c|
‘JDoIt’
end

class MyRubyClass
include JDoIt

def doIt(array_list)
r_val = []
array_list.each do |s|
r_val << s + “: Rubified!!”
end
r_val
end
end

#The last line is returned by the scripting container
MyRubyClass.new

-----------END _RUBY

The following java interface:

package foo;

import java.util.List;

public interface DoIt {
List doIt(List javaData);
}

--------END_JAVA_INTERFACE

The following java class:

package foo;

import org.jruby.embed.ScriptingContainer;
import java.util.*;

public class Toy {

public void runScript() {
ScriptingContainer container = new ScriptingContainer();
DoIt it =
(DoIt)container.runScriptlet(org.jruby.embed.PathType.ABSOLUTE,“C:/Users/Cris/workspace_s/Silly/src/foo.rb”);
List javaData = new ArrayList();
javaData.add(“Hello World!”);
javaData.add(“Hi World!”);
javaData.add(“Howdy World!”);
List rubifiedData = (List)it.doIt(javaData);
for (String s : rubifiedData) {
System.out.println(s);
}
}

public static void main(String[] args){
Toy t = new Toy();
t.runScript();
}

}
-----END_JAVA

The output from this program is:

Hi world from the land of Ruby!
Hello World!: Rubified!!
Hi World!: Rubified!!
Howdy World!: Rubified!!

Make sure to have jruby’s complete jar on your build and classpath.

Cris


#6

Ronald,

You can pass arbitrary java objects. In my case, I arbitrarily passed
in an array list, and using this implementation I obfuscated that fact:

def doIt(array_list)
r_val = []
array_list.each do |s|
r_val << s + “: Rubified!!”
end
r_val
end

JRuby can use dark magic to do this, I could just as easily do this:

def doIt(array_list)
# r_val = []
# array_list.each do |s|
# r_val << s + “: Rubified!!”
# end
# r_val
r_val = java.util.ArrayList.new
it = array_list.iterator
while (it.hasNext)
n = it.next
r_val.add(n << “: Still rubified!!!”)
end
r_val
end

now I am treating the ArrayList as any old POJO.

Things get dicey if we have overloaded methods in java as Ruby does not
allow this. If you run into this problem look up java_send. Here is a
quick example:

irb(main):001:0> a = java.lang.StringBuilder.new
=> #Java::JavaLang::StringBuilder:0x443118b0
irb(main):002:0> a.java_send :append, [java.lang.String], “I am Here\n”
=> #Java::JavaLang::StringBuilder:0x443118b0
irb(main):003:0> puts a
I am Here
=> nil
irb(main):004:0> a.java_send :append, [java.lang.CharSequence], “I am
not here!\n”
=> #Java::JavaLang::StringBuilder:0x443118b0
irb(main):005:0> puts a
I am Here
I am not here!

As far as bundling with a jar…

making the following change in the runScript method:

//DoIt it =

(DoIt)container.runScriptlet(org.jruby.embed.PathType.ABSOLUTE,“c:\temp\foo.rb”);
DoIt it =
(DoIt)container.runScriptlet(org.jruby.embed.PathType.CLASSPATH,“foo.rb”);

and placing foo.rb in a jar file on the classpath will work.

The only way I can think of to completely hide your ruby source code is
to place the string in a java file and call the runScriptlet method with
a string argument.

If I take my original foo.rb and compile it here is the decompiled code:

import org.jruby.Ruby;
import org.jruby.ir.IRScope;
import org.jruby.ir.runtime.IRRuntimeHelpers;

public class foo
{

public static void main(String args[])
{
    Ruby ruby = Ruby.newInstance();
    ruby.runInterpreter(IRRuntimeHelpers.decodeScopeFromBytes(ruby,

script_ir.getBytes(“ISO-8859-1”), “foo.rb”));
}

public static IRScope loadIR(Ruby ruby, String s)
{
    return IRRuntimeHelpers.decodeScopeFromBytes(ruby,

script_ir.getBytes(“ISO-8859-1”), s);
}

private static final String script_ir = (new

StringBuilder()).append("\000\000\000\001\000\000\0014\377\377\377\377\016\b\023t\000\000Nt\006\001s\000Nt\005\002_\000?\000Nt\000\003{\000fNl\001h\000t\000\003?\001Gt\000\004f\030f/f\037\000\004to_at\000\004\000t\000\005\024\000\004eacht\000\005\377\377\377\377\377wS\001t\000\006?\005\035\001\001pS\001l\001h\000t\000\007%t\000\007\0232L\025_GLOBAL_ENSURE_BLOCK_\000\b\023t\004\000\001\tl\001i\000\000Nt\006\001s\000Nt\005\002_\0001L\016_CLOSURE_START\000?\002\033\000\001-l\001i\000\001f\030t\004\003\001\033\000\001-l\001i\000\001f\004t\004\004\001\033\000\001%t\004\004\001\001f\030t\004\005\001\026l\001h\001\003[]=\002t\004\003\001t\004\005\001%t\004\005\00131L\025_GLOBAL_ENSURE_BLOCK_\000\020t\004\006\001Wt\004\007\001\002\001t\004\006\001%t\004\007\0011L\007CL1_LBL\000\002\007\006foo.rb\000\b\000\000\001\001h\000\001\001h\000\f\000\020foo.rb_CLOSURE_1\001\b\003\f_CLOSURE_END\001\016_CLOSURE_START\001\007CL1_LBL\001\000\377\000\000\002\000\000\000\000\000\001\001\001i\377\000\000\002\000\000\000\000\000\001\001i\000p").toString();

}

We can see script_ir is private and taunting us.

You could probably write a ruby script that converts ruby scripts to be
member escaped strings in a java file if it is that
important.

Good Luck,

Cris


#7

Cris S. wrote in post #1179389:

OK…

How is this:
[…]
public interface DoIt {
List doIt(List javaData);
}
[…]
public void runScript() {
ScriptingContainer container = new ScriptingContainer();
DoIt it =

(DoIt)container.runScriptlet(org.jruby.embed.PathType.ABSOLUTE,“C:/Users/Cris/workspace_s/Silly/src/foo.rb”);

List<String> javaData = new ArrayList<String>();
javaData.add("Hello World!");
javaData.add("Hi World!");
javaData.add("Howdy World!");
List<String> rubifiedData = (List<String>)it.doIt(javaData);

Well, two questions here:

Your example passes only strings to the Ruby class. In my case, I need
to pass arbitrary Java objects to Ruby. Would this work too, and how
would I have to define javaData? Can it be simply a List?

Further, I’m surprised that you refer to the Ruby class by mentioning
the file containing the Ruby source code (foo.rb) inside your main
program. I thought we must (can?) compile the Ruby program to Java using
jrubyc, and deliver only ‘class’ or ‘jar’ files. While this is not a
strict requirement, I would, if possible, prefer delivering only
compiled code, not source code. Can this be done?

Ronald


#8

Ronald,

https://pragprog.com/book/jruby/using-jruby

That link is for Charles Oliver N.'s JRuby book.

Charles is essentially “Mr. JRuby”. He will cover having Ruby call into
Java. Sadly the other direction isn’t covered well. The third time I
read his book I found that one sentence that made me have that ‘aha
moment’ and I had a java object call into ruby for the first time.

The scripting container stuff…

I downloaded the JRuby source and stared at it until I could get it to
do what I needed it to do, which happens to be very near what you need.

Good Luck,

Cris


#9

Thanks a lot for the great example!!! I will take it as a base for my
work.

I wonder whether there is a good tutorial to learn this kind of things.
I found several JRuby tutorials, but they covered mainly the case where
JRuby is using some Java class, or where a JRuby script is called from
Java without much parameter passing. The JRuby API docs contain example
for certain classes, but the great picture is missing somehow.

Can you recommend a good introductory source for people who already know
Ruby well, know a little bit of Java, and need to learn about the
interaction of those two?

Ronald