Sunday, March 30, 2008

Tips for JRuby engine: invokeMethod usage

This is the fourth post of "Tips for JRuby engine" series I've written in this blog and shows how to use javax.script.Invocable#invokeMethod() method defined in JSR 223 APIs. The idea to invoke Ruby's methods from Java is identical to the one described in the former post. It would greatly help you to understand about arguments and retuen values of invokeMethod() method.

As I explained in the former post, invokeMeothd() is used when Ruby's methods to be invoked from Java are resides in classes or modules. Except the first argument, invokeMethod() usage is the same as the one of invokeFunction(). The first argument is an instance of a Ruby class that has a method to be invoked. Consequently, Ruby code must return the instance so that Java can identify whose method it is and then invoke it. Ruby's instance can be get by a returned value when the Ruby script is evaled; therefore, before using invokeMethod(), a programmer must have the line, "Object object = engine.eval(script);", in his or her code to get the instance. The simplest code might be look like this:
String script =
"module SomeModule\n" +
"def say()" +
"puts \"Hi, there!\"" +
"end\n" +
"end\n" +
"class SomeClass\n" +
"include SomeModule;" +
"end\n" +
"SomeClass.new";
Object object = engine.eval(script);
Invocable invocable = (Invocable) engine;
invocable.invokeMethod(object, "say");
script =
"class AnotherClass\n" +
"def say_it_again()" +
"puts \"OK. I said, \'Hi, there.\'\"" +
"end\n" +
"end\n" +
"AnotherClass.new";
object = engine.eval(script);
invocable.invokeMethod(object, "say_it_again");
The first script has a method defined in a module and returns an instance of a class that includes the module as well as the method. The return value of JSR 223 APIs' eval() method should be the Ruby created instance and be the first argument of invokeMethod(). The second script is simpler than the first one - a class has a method and returns an instance. After evaluatig the script, Ruby defined method gets run over invokeMethod() method by using the returned instance.

If a programmer want to get more than one instance from single eval() method of a script, how can he or she get those instacnes from Ruby? We have two possiple ways of doing this; one is to get java.util.List typed object as a return value, and another is to use gobal variables. Ruby allows us to return more than one value at a time, so we can get multiple instances Ruby created by elements of List typed object. The folloing snippet shows how to get multiple instaces and invoke Ruby's method over Java's invokeMethod():
String script = 
"class Flowers\n" +
"@@hash = {\'red\' => \'ruby\', \'white\' => \'pearl\'}\n" +
"def initialize(color, names)" +
"@color = color;" +
"@names = names;" +
"end\n" +
"def comment\n" +
"puts \"#{@names.join(\', \')}. Beautiful like a #{@@hash[@color]}!\"" +
"end\n" +
"def others(index)" +
"print \"If I omit #{@names[index]}, \";" +
"@names.delete_at(index);" +
"print \"others are #{@names.join(\', \')}.\n\"" +
"end\n" +
"end\n" +
"red = Flowers.new(\"red\", [\"cameliia\", \"hibiscus\", \"rose\", \"canna\"])\n" +
"white = Flowers.new(\"white\", [\"gardenia\", \"lily\", \"daisy\"])\n" +
"return red, white";
Object objects = engine.eval(script);
Invocable invocable = (Invocable) engine;
if (objects instanceof List) {
for (Object object : (List)objects) {
invocable.invokeMethod(object, "comment");
invocable.invokeMethod(object, "others", 1);
}
}

The Ruby script above defines two methods, comment and others, in the Flowers class. Just below the class definition, it created two instances whose names are red and white, then returns both instances at a time. In Java code, two instaces must be returned by packing in java.lang.Object typed single instance; thus, we should cast it into java.util.List type and take each instance out from it. Each instance is set to the first argument of invokeMethod().

Another way of getting Ruby created multiple instaces is to use global variables. Instead of returning values, instances are given over to Java by delaring them global variables. The snippet below shows how to do this:
String script = 
"class Flowers\n" +
"@@hash = {\'red\' => \'ruby\', \'white\' => \'pearl\'}\n" +
"def initialize(color, names)" +
"@color = color;" +
"@names = names;" +
"end\n" +
"def comment\n" +
"puts \"#{@names.join(\', \')}. Beautiful like a #{@@hash[@color]}!\"" +
"end\n" +
"def others(index)" +
"print \"If I omit #{@names[index]}, \";" +
"@names.delete_at(index);" +
"print \"others are #{@names.join(\', \')}.\n\"" +
"end\n" +
"end\n" +
"$red = Flowers.new(\"red\", [\"cameliia\", \"hibiscus\", \"rose\", \"canna\"])\n" +
"$white = Flowers.new(\"white\", [\"gardenia\", \"lily\", \"daisy\"])";
engine.eval(script);
Object red = engine.get("red");
Object white = engine.get("white");
Invocable invocable = (Invocable) engine;
invocable.invokeMethod(red, "comment");
invocable.invokeMethod(white, "comment");
invocable.invokeMethod(red, "others", 1);
invocable.invokeMethod(white, "others", 2);

Ruby's class difinition is exactly identical to the one that returns multiple instances. The differences are the last part that global variables, red and white, are used to assign instacnes. In Java code, two instances are get through JRuby engine's context.

Please read my former post to know how to get return value from method invocations or how to use global variables in methods.

Following code is an enitre one to perfome snippets illustrated here:
package canna;

import java.util.List;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class InvokingMethodsExample {
private InvokingMethodsExample()
throws ScriptException, NoSuchMethodException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
invokeSimpleMethod(engine);
invokeMethodWithMultipleInstances(engine);
invokeMethodWithGlobalVariables(engine);
}

private void invokeSimpleMethod(ScriptEngine engine)
throws ScriptException, NoSuchMethodException {
String script =
"module SomeModule\n" +
"def say()" +
"puts \"Hi, there!\"" +
"end\n" +
"end\n" +
"class SomeClass\n" +
"include SomeModule;" +
"end\n" +
"SomeClass.new";
Object object = engine.eval(script);
Invocable invocable = (Invocable) engine;
invocable.invokeMethod(object, "say");
script =
"class AnotherClass\n" +
"def say_it_again()" +
"puts \"OK. I said, \'Hi, there.\'\"" +
"end\n" +
"end\n" +
"AnotherClass.new";
object = engine.eval(script);
invocable.invokeMethod(object, "say_it_again");
}

private void invokeMethodWithMultipleInstances(ScriptEngine engine)
throws ScriptException, NoSuchMethodException {
String script =
"class Flowers\n" +
"@@hash = {\'red\' => \'ruby\', \'white\' => \'pearl\'}\n" +
"def initialize(color, names)" +
"@color = color;" +
"@names = names;" +
"end\n" +
"def comment\n" +
"puts \"#{@names.join(\', \')}. Beautiful like a #{@@hash[@color]}!\"" +
"end\n" +
"def others(index)" +
"print \"If I omit #{@names[index]}, \";" +
"@names.delete_at(index);" +
"print \"others are #{@names.join(\', \')}.\n\"" +
"end\n" +
"end\n" +
"red = Flowers.new(\"red\", [\"cameliia\", \"hibiscus\", \"rose\", \"canna\"])\n" +
"white = Flowers.new(\"white\", [\"gardenia\", \"lily\", \"daisy\"])\n" +
"return red, white";
Object objects = engine.eval(script);
Invocable invocable = (Invocable) engine;
if (objects instanceof List) {
for (Object object : (List)objects) {
invocable.invokeMethod(object, "comment");
invocable.invokeMethod(object, "others", 1);
}
}
}

private void invokeMethodWithGlobalVariables(ScriptEngine engine)
throws ScriptException, NoSuchMethodException {
String script =
"class Flowers\n" +
"@@hash = {\'red\' => \'ruby\', \'white\' => \'pearl\'}\n" +
"def initialize(color, names)" +
"@color = color;" +
"@names = names;" +
"end\n" +
"def comment\n" +
"puts \"#{@names.join(\', \')}. Beautiful like a #{@@hash[@color]}!\"" +
"end\n" +
"def others(index)" +
"print \"If I omit #{@names[index]}, \";" +
"@names.delete_at(index);" +
"print \"others are #{@names.join(\', \')}.\n\"" +
"end\n" +
"end\n" +
"$red = Flowers.new(\"red\", [\"cameliia\", \"hibiscus\", \"rose\", \"canna\"])\n" +
"$white = Flowers.new(\"white\", [\"gardenia\", \"lily\", \"daisy\"])";
engine.eval(script);
Object red = engine.get("red");
Object white = engine.get("white");
Invocable invocable = (Invocable) engine;
invocable.invokeMethod(red, "comment");
invocable.invokeMethod(white, "comment");
invocable.invokeMethod(red, "others", 1);
invocable.invokeMethod(white, "others", 2);
}

public static void main(String[] args) throws ScriptException, NoSuchMethodException {
new InvokingMethodsExample();
}
}

If this code gets run successfully, it produces outputs below:
Hi, there!
OK. I said, 'Hi, there.'
cameliia, hibiscus, rose, canna. Beautiful like a ruby!
If I omit hibiscus, others are cameliia, rose, canna.
gardenia, lily, daisy. Beautiful like a pearl!
If I omit lily, others are gardenia, daisy.
cameliia, hibiscus, rose, canna. Beautiful like a ruby!
gardenia, lily, daisy. Beautiful like a pearl!
If I omit hibiscus, others are cameliia, rose, canna.
If I omit daisy, others are gardenia, lily.

Sunday, March 09, 2008

Tips for JRuby engine: how to invoke Ruby's methods

This is the third post of "Tips for JRuby engine" series I've written in this blog. This post is focused on how Java can invoke Ruby defined methods. Users of JRuby engine might want to have Ruby as their first language to process complicated issues and use Ruby's methods in Java. Or, they already have a bunch of methods written in Ruby and feel happy if those methods are also available to reuse in Java code without any modification. JSR 223 scripting APIs have javax.script.Invocable interface and invokeFunction/invokeMethod methods defined in it. This interface is designed to enable users to invoke procedures and functions defined by dynamic languages from Java. The method, invokeFunction(), is used when a method is defined outside of classes or modules, which is known as a top-level method in Ruby. On the other hand, invokeMethod() method is applied when the method is defined in a class or module. In this post, I'll write about how to use invokeFunction() method, and the next post will be the one about invokeMethod().

To invoke Ruby defined top-level methods, a programmer need to eval scripts prior to use the invokeFunction() method. JRuby engine doesn't know the method specified in the first argument of invokeFunction() really exists and is ready to use unless it evals the script that has the definition of the method. Therefore, two steps are required to run Ruby's methods over JSR 223 APIs - first, eval(), and second, invokeFunction(). The most simple method invocation would be like this:
String script = 
"def say_something()" +
"puts ¥"I¥'m sleepy since it¥'s spring!¥"" +
"end";
engine.eval(script);
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("say_something");

As for JRuby engine, "new Object[]{}" or "null" can be used as a second argument if Ruby's method doesn't need any argument. Compiler might complain about it, but the code works.

The next snippet shows the way of invoking the method with arguments. If people look at JDK 1.6 API document, they will know that invokeFunction() method can have multiple arguments to give over to Ruby's method. Argumnets are either a simple object array or varargs. A method invocation with arguments would look like:
String script =
"def come_back(type, *list)" +
"print ¥"#{type}: #{list.join(¥',¥')}¥";" +
"print ¥"...¥";" +
"list.reverse_each {|l| print l, ¥",¥"};" +
"print ¥"¥n¥";" +
"end";
engine.eval(script);
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("come_back",
"sol-fa",
"do", "re", "mi", "fa", "so", "ra", "ti", "do");

The first argument of invokeFunction() method is the name of Ruby's method to invoke: rest of all arguments are the ones that Ruby's method needs to execute.

Then, a simple question might be come up with - how can I get return values over a method invocation? The JSR 223 method, invokeFunction() returns a single value whose type is java.lang.Object. This means that a programmer can get any type of Java object after executing Ruby's method if Ruby returns value(s). It is simple to get a single object as the return value since a Ruby’s object is mapped to the same type of a Java object. The perplexities might come from that Ruby’s method can return multiple values, not only one like Java. In this case, return values are elements of an array object; therefore, java.util.List typed object would be returned, and the code would look like the one below:
String script =
"def get_by_value(hash, value)" +
"hash.select { |k,v| v == value }" +
"end";
engine.eval(script);
Invocable invocable = (Invocable) engine;
Map map = new HashMap();
map.put("ruby", "red");
map.put("pearl", "white");
map.put("rhino", "gray");
map.put("rose", "red");
map.put("nimbus", "gray");
map.put("gardenia", "white");
map.put("camellia", "red");
Object object = invocable.invokeFunction("get_by_value", map, "red");
System.out.print( "red: ");
if (object instanceof List) {
for (Object element: (List)object) {
if (element instanceof List) {
for (Object param : (List)element) {
System.out.print(param + " ");
}
}
}
}
System.out.println();

So far, programmers could come to invoke Ruby's method from Java satisfactorily, but should they put all objects along with in the arguments' row of invokeFunction() method? Even if the programmers can put multiple arguments whatever they need to run Ruby's method correctly, it is confusing as the number of arguments increases. Using global variables would help to reduce putting many arguments. Here's a example code that reduced arguments from above snippet by using a global variable:
String script =
"def get_by_value(value)" +
"$hash.select { |k,v| v == value }" +
"end";
engine.put("hash", map);
engine.eval(script);
Invocable invocable = (Invocable) engine;
Object object = invocable.invokeFunction("get_by_value", "white");

The code below is an entire class to perform all of above snippets:
package canna;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class InvokingFunctionsExample {
private InvokingFunctionsExample()
throws ScriptException, NoSuchMethodException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
invokeSimpleFunction(engine);
invokeFunctionWithArguments(engine);
Map map = new HashMap();
map.put("ruby", "red");
map.put("pearl", "white");
map.put("rhino", "gray");
map.put("rose", "red");
map.put("nimbus", "gray");
map.put("gardenia", "white");
map.put("camellia", "red");
invokeFunctionWithReturns(engine, map);
invokeFunctionWithGlobalVariables(engine, map);
}

private void invokeSimpleFunction(ScriptEngine engine)
throws ScriptException, NoSuchMethodException {
String script =
"def say_something()" +
"puts \"I\'m sleepy because I went to bed three in the morning!\"" +
"end";
engine.eval(script);
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("say_something");
}

private void invokeFunctionWithArguments(ScriptEngine engine)
throws ScriptException, NoSuchMethodException {
String script =
"def come_back(type, *list)" +
"print \"#{type}: #{list.join(\',\')}\";" +
"print \"...\";" +
"list.reverse_each {|l| print l, \",\"};" +
"print \"\n\";" +
"end";
engine.eval(script);
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("come_back",
"sol-fa",
"do", "re", "mi", "fa", "so", "ra", "ti", "do");
}

private void invokeFunctionWithReturns(ScriptEngine engine, Map map)
throws ScriptException, NoSuchMethodException {
String script =
"def get_by_value(hash, value)" +
"hash.select { |k,v| v == value }" +
"end";
engine.eval(script);
Invocable invocable = (Invocable) engine;
Object object = invocable.invokeFunction("get_by_value", map, "red");
printValues("red", object);
object = invocable.invokeFunction("get_by_value", map, "gray");
printValues("gray", object);

}

private void printValues(String value, Object object) {
System.out.print(value + ": ");
if (object instanceof List) {
for (Object element: (List)object) {
if (element instanceof List) {
for (Object param : (List)element) {
System.out.print(param + " ");
}
}
}
}
System.out.println();
}

private void invokeFunctionWithGlobalVariables(ScriptEngine engine, Map map)
throws ScriptException, NoSuchMethodException {
String script =
"def get_by_value(value)" +
"$hash.select { |k,v| v == value }" +
"end";
engine.put("hash", map);
engine.eval(script);
Invocable invocable = (Invocable) engine;
Object object = invocable.invokeFunction("get_by_value", "white");
printValues("white", object);
}

public static void main(String[] args)
throws ScriptException, NoSuchMethodException {
new InvokingFunctionsExample();
}
}

When this code gets run successfully, it will produce following outputs.
I'm sleepy because I went to bed three in the morning!
sol-fa: do,re,mi,fa,so,ra,ti,do...do,ti,ra,so,fa,mi,re,do,
red: rose red camellia red ruby red
gray: rhino gray nimbus gray
white: gardenia white pearl white

Tow methods defined in JSR 223 APIs, invokeFunction() and invokeMethod(), are powerful, but have a little API flaw. We can't give a block over when we invoke Ruby's methods from Java. For example, Ruby allows us to use "yield" in a method and give different blocks needed to run a bit differently:
def search(array)
for item in array
return item if yield(item)
end
end
result = search(["camellia", "gardenia", "nimbus"]) {|str| str[0] ==?c}
print result, "\n"

Unofortunately, we don't have any way of doing this.

Saturday, March 08, 2008

Tips for JRuby engine: how to share objects mutually

This is the second tips for JRuby engine in addition to the first one. In this post, I'll show you how to share the same instances in both Java and Ruby, in other words, referring Java-created objects in Ruby, and contrarily, Ruby-created objects in Java.

To refer the same instances in both languages, a global variable is a key concept. If a coder have experienced Ruby programming, he or she must know what is a Ruby's global variable. In Ruby, a name of a global variable is defined to start with a symbol, '$,' such as $name or $file. An object that Java instantiated is always referred as the global variable in Ruby code; consequently, Java-created object's name must start with '$' in a script. The name of a global variable is exactly the same as the attribute name in Java, but those names don't start with '$'. Instead, the name taken off the beginning '$' character such as "name" or "file" is used for Java's attribute name.

For example, suppose a java.util.List typed instance is created and set to the context of a script engine with the attribute name, "list" by using put() method in Java. Then, Ruby runtime knows that the global variable, $list, and the associated instance exist. Here's code snippets that demonstrates how the instance is created and refered:

List list = new ArrayList();
list.add("What's up?");
list.add("How're you doing?");
list.add("How have you been?");
engine.put("list", list);
String script = "$list.each {|msg| puts msg }";
engine.eval(script);


If a coder created an object in Ruby and wanted to refer it in Java, he or she should name the instance like $something in Ruby and retrieve it from engine's context by the name, "something". Here's another snippets that shows how to do this:

String script = "$seasons = ['spring', 'summer', 'fall', 'winter']";
engine.eval(script);
List seasons = (List) engine.get("seasons");
for (String season : seasons) {
System.out.println(season);
}


Like this, global variables are often used to share the same instance in both languages; moreover, we have one more way to share a Ruby-created instance in Java. This is done by returning a value. In this case, both Ruby and Java don't use any global varibale while Ruby takes local variables, and Java assigns them to whatever it needs. Suppose Ruby code returns an array whose local variable name is colors, then Java receives a java.util.List typed object as a return value by invoking script engine's eval method(). The snippets is shown below:

script = "colors = ['red', 'green', 'white', 'blue'];" +
"colors.reverse";
List colors = (List) engine.eval(script);
for (String color : colors) {
System.out.println(color);
}

Although only the first line of the script is enough to get the return value from Ruby, I added the second line because it is too short for users to understand the script is Ruby code.

As I talked about, we have two ways to share instances between Ruby and Java by using a JSR 223 scripting engine. If you use global variables, you can share multiple instances at a time. On the other hand, you can share only one instance when you do it by returning value. However, sometimes, users need to get a returned value after processing some algorithm, so the choice is on the users. Of course, users can mix two ways to share instances.

The code below is an entire class used for this tips:
package canna;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ReferringObjectsExample {

private ReferringObjectsExample() throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
createObjectsInJava(engine);
createObjectsInRuby(engine);
}

private void createObjectsInJava(ScriptEngine engine) throws ScriptException {
// giving an object to Ruby as a global variable
List list = new ArrayList();
list.add("What's up?");
list.add("How're you doing?");
list.add("How have you been?");
engine.put("list", list);
String script = "$list.each {|msg| puts msg }";
engine.eval(script);
engine.put("first", 2008);
script = "$first.step(2015, 2) {|i| puts i }";
engine.eval(script);
}

private void createObjectsInRuby(ScriptEngine engine) throws ScriptException {
// referring an object as a global variable
String script = "$seasons = ['spring', 'summer', 'fall', 'winter']";
engine.eval(script);
List seasons = (List) engine.get("seasons");
for (String season : seasons) {
System.out.println(season);
}
// receiving an array object returned from Ruby
script = "colors = ['red', 'green', 'white', 'blue'];"+
"colors.reverse";
List colors = (List) engine.eval(script);
for (String color : colors) {
System.out.println(color);
}
// receiving a hash object returned from Ruby
script = "gpas1 = {\"Alice\" => 3.75, \"Bob\" => 4.0};"+
"gpas2 = {\"Alice\" => 3.92, \"Chris\" => 3.55};"+
"gpas1.merge!(gpas2)";
Map gpas = (Map)engine.eval(script);
for (String name : gpas.keySet()) {
System.out.println(name +": " + gpas.get(name));
}
}

public static void main(String[] args) throws ScriptException {
new ReferringObjectsExample();
}
}
When the code gets run, it outputs like this:

What's up?
How're you doing?
How have you been?
2008
2010
2012
2014
spring
summer
fall
winter
blue
white
green
red
Alice: 3.92
Bob: 4.0
Chris: 3.55

Thursday, March 06, 2008

Tips for JRuby engine: how to get engine's instance

Writing a code by using scripting API introduced in JDK 6 might be enjoyable because it enables us to use both dynamic languages and Java at a time, smoothly. Moreover, it enables to mix multiple dynamic languages without writing any Java code. For example, Rhino can execute Ruby code over scripting API. However, documents are few, unfortunately, and users often confuse how to use the API. In light of this bad situation, I'll try to give users some tips about JRuby engine to possibly help them out from annoying exceptions and errors.

The first tips are how to get an engine's instance, which is a prerequisite to get scripts run. Scripting API has a couple of methods available. As for JRuby engine, following three ways work well for getting javax.script.ScriptEngine typed instance:

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
or

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("ruby");
or

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByExtension("rb");

Users can get engine's instance by one of these. Although scripting API defines ScriptEngineManager#getEngineByMimeType() method, no coder can use this method. Since JRuby engine doesn't define its mime types obtainable, users don't have any parameter to pass to.

Here's an entire example code for the first step:
package canna;

import java.util.List;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class GettingEngineExample {
GettingEngineExample() throws ScriptException {
caseOne();
caseTwo();
caseThree();
caseFour();
}

void caseOne() throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
ScriptEngineFactory factory = engine.getFactory();
System.out.println("engine name: " + factory.getEngineName());
System.out.println("version: " + factory.getEngineVersion());
engine.eval("puts 'Case one: getEngineByName(\"jruby\").'");
}

void caseTwo() throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("ruby");
ScriptEngineFactory factory = engine.getFactory();
System.out.println("engine name: " + factory.getEngineName());
System.out.println("version: " + factory.getEngineVersion());
engine.eval("puts 'Case two: getEngineByName(\"ruby\").'");
}

void caseThree() throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByExtension("rb");
ScriptEngineFactory factory = engine.getFactory();
System.out.println("engine name: " + factory.getEngineName());
System.out.println("version: " + factory.getEngineVersion());
engine.eval("puts 'Case three: getEngineByExtension(\"rb\").'");
}

void caseFour() {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByExtension("rb");
ScriptEngineFactory factory = engine.getFactory();
List names = factory.getMimeTypes();
System.out.println("mime types are ...");
for (String name : names) {
System.out.print(name);
}
}

public static void main(String[] args) throws ScriptException {
new GettingEngineExample();
}
}
This code outputs like this:

engine name: JRuby Engine
version: 1.1.1
Case one: getEngineByName("jruby").
engine name: JRuby Engine
version: 1.1.1
Case two: getEngineByName("ruby").
engine name: JRuby Engine
version: 1.1.1
Case three: getEngineByExtension("rb").
mime types are ...

JRuby 1.1 RC3 is about to release

A few days have passed since the version name 1.1RC3 disappeared from issue jira of JRuby. And, Charles Nutter posed what would be showstoppers for final RC verison, RC3 to jruby-dev ml. So, we could know JRuby 1.1RC3 is about to released in a couple of days.
How about JRuby engine? As far as I tested JRuby engine by using trunk version, JRuby engine should not need any bug fixes to catch up with a new release of JRuby. Updating README.TXT would be enough. Easy, this time.

Wednesday, March 05, 2008

Download site just for JRuby engine

Today, I created the download site just for JRuby eninge at scripting.dev.java.net. I've thought this kind of site should have existed since I updated JRuby engine for JRuby 1.1RC1. As for current release style of the scripting project, only one binary archive of each dynamic language is ready to download. This style might have been enough before and easy to release a bunch of engines for individual dynamic languages at a time. However, dynamic languages have had multiple versions which require different implementations of engines recently.
A request from a user made me start working on it yesterday. Looking around links to help pages in the project home, I discovered that creating a new page under the project in dev.java.net was quit easy. Just clicking on the link and filling out a small form. That's it.
From now on, I can update JRuby engine's binary archives compliant to the latest JRuby anytime.

Monday, March 03, 2008

JSR223 JRuby engine update

I fixed one more bug to make JRuby engine work on JRuby 1.1RC2 yesterday, so I updated CVS today. Now, the latest version in CVS repo is compliant to JRuby 1.1RC2.

I figured out that another bug was caused by newly added org.jruby.util.io.ChannelStream class to JRuby. ChannelStream has a sync member which is set to be false by default and flushes the output buffer only when the sync's value is true, or huge strings are set to output buffer. A ChannelStream type instance used in an ordinary eval process has the true value in its sync; however, it has false when it is used from the process starts from IRubyObject#callMethod(). I fixed this by forcibly setting true to the sync since I couldn't figure out when and by whom its value is changed into true in JRuby.

I encountered further bug when I ran examples on Mac OS X. This should be the bug of JDK 1.6 for Max OS X because it never happens on Ubuntu. When a big Ruby script is pared by the parseEval method, a part of script seems to be truncate because JRuby raises a syntax error though Ruby scripts are written in completely correct syntax. JRuby engine has a way of avoiding this bug which is to set a filename of the Ruby script to JRuby engine's context by using a "ScriptEngine.FILENAME" property.

My next work would be to write more examples and tests to know how perfect the engine works with current and future versions of JRuby.