Reputation: 175
I have a Java application that depends on the result from a ruby script. The issue I'm facing is that this ruby script is called many times which requires loading the script's libraries every time it's executed.
I'm not familiar with Ruby, but in my search I've come across JRuby. Since it interprets ruby code and runs it on the JVM, my initial thought was "great, I can just compile the ruby script to .class files package it up and interact with it like regular java objects". This would avoid the overhead that came with loading the libraries each time I executed the ruby script.
After looking deeper into jruby, I'm understanding it doesn't work this way. So to do what I want I can use JRuby's JavaEmbedUtils to
This is what I had in mind (I'll be testing soon)
// 1
List<String> paths = new ArrayList<>();
paths.add(".");
Ruby runtime = JavaEmbedUtils.initialize(paths);
// 2
String script = "/path/to/script.rb";
IRubyObject recvr = JavaEmbedUtils.newRuntimeAdapter().eval(runtime, script);
// 3 Call this many times
JavaEmbedUtils.invokeMethod(runtime, receiver, "method", null, null);
Is my understanding correct in that this approach allows me to use the contents of the script many times while loading the script's libraries once? Is there an alternate or more JRuby way of doing what i'm looking for?
UPDATE
So I tested something similar to what Eugene suggested (a) i.e. ScriptingContainer and compared that to calling the script using (b) java.lang.Runtime for a total of 30 runs.
Upvotes: 2
Views: 2957
Reputation: 1300
This is how I use JRuby in a scripting container from java code:
import org.jruby.*;
import org.jruby.embed.LocalVariableBehavior;
import org.jruby.embed.PathType;
import org.jruby.embed.ScriptingContainer;
ScriptingContainer container = new ScriptingContainer(LocalVariableBehavior.PERSISTENT);
container.setCompileMode(RubyInstanceConfig.CompileMode.OFF);
container.setNativeEnabled(false);
container.setObjectSpaceEnabled(true);
container.put("some_param", someValue);
// My script return an array - tweak to fit your returning value
RubyArray resourceArray = (RubyArray) container.runScriptlet(PathType.CLASSPATH, scriptPath);
I have to note that JRuby is too slow on launch (these numbers is not too precise but in my case it is 2-3 sec. even on 4Ghz + SSD). TDD turns into pain with such delays. That's why there are some tweaks as object space, etc.
Also, I had to stub out this module so the rest of test uses other fixtures and run without launching JRuby. Other words, I don't have to launch it while running another test separately.
P.S.
Is my understanding correct in that this approach requires me to only load the script's dependencies once?
I'm not sure what do you mean here, but it seems all the dependencies "required" on JRuby side will be loaded every time while using this approach.
Anyway, I would make a benchmark before. Maybe it isn't worth to be thought about.
UPD:
This was more simple than I thought:
common.rb:
puts "common"
script.rb:
require 'common'
puts "script"
code:
ScriptingContainer container = new ScriptingContainer(LocalVariableBehavior.PERSISTENT);
for (int i = 0; i < 3; i++) {
container.runScriptlet(PathType.CLASSPATH, "script.rb");
}
Output:
common
script
script
script
Upvotes: 3