Monday, November 03, 2008

Recent update of JRuby engine

Recently, I updated CVS repo of JSR 223 JRuby engine to fix issue 39 and 40 filed in the issue tracker.

Issue 39 was i18n related bug. To determine a character encoding for Writer type instances, I have put sun.jnu.encoding before file.encoding. However, I knew that some platforms had different values in thier sun.jnu.encoding property from file.encoding by the bug report. Since I don't have such kind of platforms at all, I haven't noticed the bug so far. JRuby determines the output encoding from input file's encoding or applies file.encoding if Strings are direcly given to JRuby's eval method. Thus, JRuby engine shoud have seen file.encoding only.

At the same time, I added a small fix so that JRuby engine sees the encoding set in OutputStreamWriter type instances. This fix enables users to get preferable output when they execute scripts whose encodings are not default one. For example, my platform's default encoding is UTF-8, and I want to run a Ruby script written in Shift_JIS encoding, what would happen? In this case, JRuby always outputs in Shift_JIS, and JRuby script engine can't change the encoding that JRuby applies. Naturally, outputs would be unreadable characters since terminals usually display platoform's default encoding only. What if outputs are written in a file in a correct character encoding? We surely have some way of checking it. To see Shift_JIS files on UTF-8 platform, I'll execute the command "nkf -Sw file."

This is an example to use this feature:
import com.sun.script.jruby.JRubyScriptEngineManager;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Reader;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;

public class NonDefaultEncodingExample {

private String outputFilename = "output.txt";
private String errorFilename = "error.txt";
private String scriptName = "/Users/yoko/NetBeansProjects/RubyTestShiftJIS/lib/main.rb";

private NonDefaultEncodingExample() throws ScriptException, IOException {
//ScriptEngineManager manager = new ScriptEngineManager();
JRubyScriptEngineManager manager = new JRubyScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
Reader reader = new FileReader(scriptName);
OutputStreamWriter writer =
new OutputStreamWriter(new FileOutputStream(outputFilename), "Shift_JIS");
FileWriter errorWriter = new FileWriter(errorFilename);
writer.write("[outputs]\n");
errorWriter.write("[errors]\n");

ScriptContext context = new SimpleScriptContext();
context.setWriter(writer);
context.setErrorWriter(errorWriter);
context.setAttribute(ScriptEngine.FILENAME, scriptName, ScriptContext.ENGINE_SCOPE);
try {
engine.eval(reader, context);
} finally {
writer.close();
errorWriter.close();
}
}

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


It is very, very important to set filename by using ScriptEngine.FILENAME attribute name. As I wrote in the section 3. JRuby vs. JRuby script engine of my old post, http://yokolet.blogspot.com/2008/10/why-did-i-get.html, JRuby engine hands String read from Reader type instance if filename is not given. This means JRuby loses the chance to know original script's encoding and just applies platform's default encoding even though given String has another encoding. When JRuby supports Ruby 1.9.x and its encoding declaration, this not nice mechanism would disappear.

Issue 40 is related to termination control. Since javax.script API doesn't have terminate() method like BSF, a choice was to execute JRuby's terminate() method always or never execute it. Many of Ruby test scripts have at_exit block, which gets run when JRuby's terminate() method is invoked, so I chose the former one. JRuby's terminate() method terminates a local scope, too. For this reason, JRuby didn't keep local scopes over the eval methods. To improve this, I introduced com.sun.script.jruby.terminator System property, whose values is "on" or "off." When terminator is turned off, JRuby engine doesn't execute JRuby's terminate() method, so variables in the local scope are available to keep using. I tested this as in below, which was based on the code posted to jruby-users ML before:
import com.sun.script.jruby.JRubyScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class LocalScopeTest {

private LocalScopeTest() throws ScriptException {
//ScriptEngineManager manager = new ScriptEngineManager();
JRubyScriptEngineManager manager = new JRubyScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
System.setProperty("com.sun.script.jruby.terminator", "off");
engine.eval("$x='sun global'");
engine.eval("puts \"$x = #{$x}\"");
engine.eval("at_exit { puts \"in an at_exit block\" }");

engine.eval("x='sun local'");
engine.eval("puts \"x = #{x}\"");
System.setProperty("com.sun.script.jruby.terminator", "on");
engine.eval(new String());
}

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

When I ran this code, I got this output:
$x = sun global
x = sun local
in an at_exit block

Again, javax.script API doesn't have terminte() method, so JRuby engine should have some alternative to execute terminate() when it needs to be invoked. Look at the last two lines of LocalScopeTest constructor. I turned on terminator switch and evaled empty String. These are the alternative way of invoking terminate() method. By default, terminator switch is set "on", so JRuby engine should work the same as before it was. Adding this feature would not affect the program worked before. If you find bugs, feel free to file them in the issue tracker at https://scripting.dev.java.net/servlets/ProjectIssues.

This bug fixed version has not been released yet since JRuby 1.1.5 seems to be out soon. After checking JRuby engine works on upcoming JRuby 1.1.5, I will release next version.

Right after I wrote this entry, JRuby 1.1.5 was out there. So, I released JRuby engine 1.1.6 at https://scripting.dev.java.net/servlets/ProjectDocumentList?folderID=8848&expandFolder=8848&folderID=0 after I checked whether new version worked fine or not.

Wednesday, October 29, 2008

The sound of PyPy

PyPy is, of course, an implementation of Python, but its sound is a bit embarrasing for Japaense to refer to in public. The sound is the same as the one of breasts in an informal way of mentioning them, especially babies' words. If someone pronounced PyPy by Japanese accent, other people might imagine breasts although they knew he or she talked about a computer languange. Japanese surely know it is just the name in a foreign language, but another naming might have been better for Japanese to mean it in offices.

Tuesday, October 28, 2008

Why did my browser display "??????" ?


In the previous post, I wrote about some of Java’s i18n mechanisms especially when Java programs are compiled, plus read/write files. These are (1), (2), (3), illustrated in Figure 1, and we could do everything in a Java way. However, when we think about (6), (7), we need to know how communications between web containers and web browsers are going on in addition to Java’s i18n. All of you may know, communications must follow HTTP, which is defined by RFC2616(http://www.ietf.org/rfc/rfc2616.txt). HTTP has a field to save user agents' languages such as English or Spanish, but doesn't have any field for character encodings. To read and show characters correctly, we need to apply correct character set names in the right places. In this post, I'm going to write about (6) and (7) in Figure 1, which are Servlet and JSP programmings.


Typical ways of conversion – (6)/(7) Servlet and JSP

I’m going to start with a very simple combination of Tomcat and Servlet to clear the problem and solutions. The first file is a simple HTML file shown below:
[test0.html]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="ja">
<head>
<title>Simple Test 0</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript">
function escapeText() {
str = encodeURI(document.forms[0].message.value)
document.forms[0].action="/ginkgo/SimpleFormServlet0?message=" + str
document.forms[0].submit()
}
</script>
</head>
<body>
<h3>てすと0</h3>
<div>
<form onsubmit="escapeText()" method="get">
[HTTP GET] Message: <input name="message" type="TEXT" size="20"/>
</form>
</div>
<div>
<form action="/ginkgo/SimpleFormServlet0" method="post">
[HTTP POST] Message: <input name="message" type="TEXT" size="20"/>
</form>
</div>
</body>
</html>

This HTML file has two input fields, one is for HTTP GET method, another is for HTTP POST method. Even though Java based web containers can handle it, we should encode non-ASCII text typed in the input field for HTTP GET method because those are sent to the web server as a part of URI. Since RFC of HTTP defines URIs should not be non-ASCII characters, I added the escapeText() function for HTTP GET submittion. Javascript's encodeURI function converts characters into UTF-8 followed by % exactly defined in RFC. For example, Japanese characters "あいう" result in "%E3%81%82%E3%81%84%E3%81%86." On the other hand, HTTP POST method doesn't have such requirement, so we can simply send parameters without escaping them.

Sumitted parameters in the HTML file is received by the Servlet, SimpleFormServlet0.java, shown below, which does no conversion so that we can see incorrect outputs first. The servlet forwards the input text to JSP, result.jsp, after receiving the form parameter:
[SimpleFormServlet0.java]
package yellow;

import java.io.IOException;
import java.net.URLDecoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleFormServlet0 extends HttpServlet {
public static final String paramName = "message";
public static final String charset = "UTF-8";

protected void processRequest(HttpServletRequest request, HttpServletResponse response, String text)
throws ServletException, IOException {
request.setAttribute("servletName", getServletName());
request.setAttribute("inputText", text);
getServletContext().getRequestDispatcher("/result.jsp").forward(request, response);
}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String text = URLDecoder.decode(request.getParameter(paramName), charset);
processRequest(request, response, text);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response, request.getParameter(paramName));
}

@Override
public String getServletInfo() {
return "This servlet outputs incorrect characters.";
}

}

[result.jsp]
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%
String context = request.getContextPath();
String servletName = (String)request.getAttribute("servletName");
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>What did I get?</title>
</head>
<body>
<div>
<%= context + "/" + servletName %> の実行結果<hr/>
Method: <%= request.getMethod() %><br/>
Message: <%= request.getAttribute("inputText") %><br/>
</div>
</body>
</html>


When I typed Japanese characters in the input text field and submitted them, I got incorrect outputs as I expected.

What was happened inside of the web container? This result shows that Java application failed to convert characters from native encodings to Unicode. For communication between web browsers and Java applications (web containers), Java applications can't find a correct character encoding of given parameters because of HTTP. Some web containers such as IBM's WebSphere guess what encoding is appropriate in that communication and converts characters automatically; however, Tomcat, Glassfish and probably most web containers do not.

To convert submitted parameters into correct Unicode strings, we have three options. The first one is to convert them in the Servlet. Since given characters are incorrectly converted, revert them to an byte array then convert them again by using a correct character encoding. I added one method to SimpleFormServlet0 to perform this reconversion steps:
[SimpleFormServlet1.java]
package yellow;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleFormServlet1 extends HttpServlet {
public static final String paramName = "message";
public static final String charset = "UTF-8";

protected void processRequest(HttpServletRequest request, HttpServletResponse response, String text)
throws ServletException, IOException {
String inputText = getUnicodeString(text, charset);
request.setAttribute("inputText", inputText);
request.setAttribute("servletName", getServletName());
getServletContext().getRequestDispatcher("/result.jsp").forward(request, response);
}

protected String getUnicodeString(String s, String charsetName)
throws UnsupportedEncodingException {
return new String(s.getBytes("8859_1"), charsetName);

}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String text = URLDecoder.decode(request.getParameter(paramName), charset);
processRequest(request, response, text);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response, request.getParameter(paramName));
}

@Override
public String getServletInfo() {
return "This servlet outputs correct characters.";
}

}

When I changed the servlet name in document.forms[0].action of test0.html from /ginkgo/SimpleFormServlet0 to /ginkgo/SimpleFormServlet1 and tried again, I got the correct result.

The second option to convert submitted characters into correct ones is to use ServletRequest.setCharacterEncoding() method shown below:
[SimpleFormServlet2.java]
package yellow;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleFormServlet2 extends HttpServlet {
public static final String paramName = "message";
public static final String charset = "UTF-8";

protected void processRequest(HttpServletRequest request, HttpServletResponse response, String text)
throws ServletException, IOException {
request.setAttribute("servletName", getServletName());
request.setAttribute("inputText", text);
getServletContext().getRequestDispatcher("/result.jsp").forward(request, response);
}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String text = URLDecoder.decode(request.getParameter(paramName), charset);
processRequest(request, response, text);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
processRequest(request, response, request.getParameter(paramName));
}

@Override
public String getServletInfo() {
return "Correct outputs by using HttpServletRequest.setCharacterEncoding().";
}
}
This answer seems simple, but we need to care what API document says. According to the API document, this method "must be called prior to reading request parameters or reading input using getReader(). Otherwise, it has no effect." In another words, we might not be able to use this method when our web applications work on some web frameworks which internally access request object before programmers do something. Suppose I wrote an appliation on Struts and set form parameters in an ActionForm type object by using a feature of Struts, setCharacterEncoding() method would have no effect because request object had been used before I accessed it when an Action type object got executed. In this case, the first option would be more feasible choice.

The third option is to use ServletRequest.setCharacterEncoding() method in a Servlet filter as in below:
[CharacterEncodingFilter.java]
package yellow;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class CharacterEncodingFilter implements Filter {

private FilterConfig filterConfig = null;

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
try {
chain.doFilter(request, response);
} catch (Throwable t) {
t.printStackTrace();
}
}

public void destroy() {
}

public void init(FilterConfig filterConfig) {
this.filterConfig = filterConfig;
}
}
This solution would work well unless web applications or frameworks require to use some filter as the first one. In fact, filtering worked well with Struts. However, as far as I tried, setting character encoding in the filter never worked when I used HTTP GET method whereas I got correct outputs by HTTP POST method.


So far, I wrote about how to avoid getting "?????" by programming, we have more choices. Both Tomcat and Glassfish offer us a way of setting encodings by configurations. In case of Tomcat, we can set encodings by URIEncoding option of Connector configuration in serverl.xml.
<Connector port="8080" protocol="HTTP/1.1" 
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8" />

Like its name, this option affects parameters sent by HTTP GET method only. Thus, this option would be complement to Servlet filtering. Glassfish gives us a more flexible and convenient option. We can set encoding in sun-web.xml of each web application as in below:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Servlet 2.5//EN"
"http://www.sun.com/software/appserver/dtds/sun-web-app_2_5-0.dtd">
<sun-web-app error-url="">
<context-root>/poplar</context-root>
<class-loader delegate="true"/>
<jsp-config>
<property name="keepgenerated" value="true">
<description>Keep a copy of the generated servlet class' java code.</description>
</property>
</jsp-config>
<parameter-encoding default-charset="UTF-8"/>
</sun-web-app>
This configuration worked both HTTP GET and POST methods of SimpleFormServlet0 perfectly. Besides, we can set differenct character encoding in each web application on a single Glassfish server, plus editable on IDE since it is located under the WEB-INF directory. This option would be the best when we can choose Glassfish for our web container.

Let me add breif explanation about JSP. As in result.jsp above, JSP usually have both
<%@page contentType="text/html" pageEncoding="UTF-8"%>
and
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
A pageEncoding attribute in the former tag is used when a JSP file is read and compiled by Java VM, while the latter is used to render by a web browser. That is why JSP pages have both.

To determine an appropriate character encoding in communications between web containers and web browsers is complicated. Web containers might have that features, and web frameworks might give us options. However, at least we should do something when we need to handle non-ASCII characters filled in HTML form fields exactly once. If conversion is done more than once, ????? would show up on a web browser. Don't forget that!

Thursday, October 16, 2008

Why did I get "??????" ?

Recently, I saw two i18n related questions about JSR 223 scripting engine for JRuby. Reading those questions, I thought they confused how to handle non-ASCII Strings in a Java way. Java’s i18n mechanism is not so complicated, but it has several ways of encoding and decoding characters to/from Unicode. As many Java programmers know, Java VM has arrays of Unicode code points to express characters and converts them to a specific encoding such as UTF-8, Cp1250, or Shift-JIS when conversion is needed. Java programmers might not care about i18n mechanism since Java has a brilliant idea of a default encoding. However, encodings are not platform independent. I wrote this entry for those who don’t want to have “?????” outputs from Java programs anymore.

1. Basics of Unicode and encodings

First of all, programmers should read Joel Spolsky’s great article, “The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) [http://www.joelonsoftware.com/articles/Unicode.html].” This article explains a concept of Unicode, encodings and history of those features. You will surely have better ideas about Unicode, which is “just a theoretical concept.” As in the article, every single character on the earth has its own “magic number” called a code point. For example, code points for Greek characters, α, β, γ, are \u03B1, \u03B2, \u03B3, and Japanese characters, あ, い, う are \u3042, \u3044, \u3046. These code points should be converted to familiar encodings such as UTF-8 when a program needs to show human readable characters on an output window of IDEs, terminals, or web browsers. How to convert these Unicode code points from/to one of character encodings might be a culprit that causes confusion.

2. Typical ways of conversion



Java has several mechanisms to convert Unicode from/to character encodings. Look at the chart that shows typical eight ways of conversion. The chart doesn’t depict all of them, but probably covers common conversions that programmers encounter i18n troubles. Among these, I’ll pick (1), (2), (3) up in this entry because these often cause troubles while using JRuby engine.


(1) How Strings in Java programs get converted?

The first one is the most basic and most frequently used conversion performed by Java compiler. Java compiler converts human readable characters in Java programs to Unicode code points. Programmers don’t need to care about this conversion unless *.java files are saved by using platform’s default encoding and never compiled on another platform whose default encoding is not the same. Suppose someone saves a Java program with non-ASCII characters on Windows XP, and compiles it on Ubuntu. What would happen? Because default encodings of both system are not the same, he or she would have “?????” outputs if “-encoding” option is not used while compiling. A straightforward way of specifying the encoding information is, for example:

javac –encoding Cp1250 foo.java

Recently, people don’t use javac command directly but compile on IDE. As far as I know NetBenas and Eclipse can do this. On NetBeans, We can have the same effect when we select a project on Projects window, right click, select Properties, sources category, then set Encoding of *.java files. On Eclipse, when we select a project on Explorer window, right click, select Properties, Resource, check Other in Text file encoding section, then set appropriate encoding for *.java files, we can change the encoding.
Ant and Maven have options that exactly work equal to javac’s –encoding option. Ant javac task allows us to set the character encoding of *.java files by an encoding attribute. We can set the encoding in pom.xml of Maven as follows:

<build>
<plugins>
<plugin>
<artifactid>maven-compiler-plugin</artifactid>
<version>RELEASE</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>



(2)/(3) How to read/write non-ASCII characters from/to files?

When Java programmers read scripts, text or others from files, and write information, text, … to files, they should care about encodings in some cases. Again, think of the default encoding. Input files might be written on Windows platform whose default encoding is not UTF-8, and be read on Linux platform whose default encoding is UTF-8. How can Java VM convert non-ASCII characters from/to Unicode code points correctly? The one of answers is to use java.io.InputStreamReader and java.io.OutputStreamWriter. Java classes that extends Reader/Writer class are Unicode aware and converts characters automatically; however, classes except InputStreamReader/Writer can’t change the encoding from default one to another by themselves. When programmers want to set non-default encodings, followings would work:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

public class I18nFileReaderWriter {
public static void main(String[] args)
throws FileNotFoundException, UnsupportedEncodingException, IOException {
System.out.println(System.getProperty("file.encoding"));

String inputname = "input.txt";
String outputname = "output.txt";
BufferedReader reader =
new BufferedReader(new InputStreamReader(new FileInputStream(inputname), "Shift-JIS"));
BufferedWriter writer =
new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputname), "EUC-JP"));
String str;
while ((str = reader.readLine()) != null) {
System.out.println(str);
writer.write(str, 0, str.length());
writer.newLine();
}
reader.close();
writer.close();
}
}


Although my PC’s default encoding is UTF-8, I saved an input file by Shift-JIS encoding. After reading it with the conversion, the program wrote Strings out to a file by EUC-JP encoding. See? I checked each file’s encoding on FireFox, using view menu as in Figure 1 and 2.
Figure 1
Figure 2
Another good answer to read non-ASCII file is to use java.util.Scanner, which is added to JDK 5. In case that Reader type object is not required afterword, Scanner class might make code simpler than InputStreamReader as shown below:
Scanner scanner = new Scanner(new File(inputname), "Shift_JIS");
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
scanner.close();



3. JRuby vs. JRuby script engine

Strings in JRuby are not the arrays of Unicode code points although JRuby is a Java application. Because JRuby is an implementation of Ruby, JRuby has byte arrays of Strings to meet Ruby specification. About a year ago, I knew this mechanism at jruby-dev ML when I posted a question to figure out why non-ASCII characters passed from JRuby engine were not handled correctly. The answer is here, http://www.nabble.com/I18n-problem-in-StrNode-and-ByteList-to13431845.html#a13431845. In terms of a byte level, JRuby uses InputStream/OutputStream basically for its I/O while ordinary Java programs use Reader/Writer.
JSR 223 JRuby script engine follows the ordinary Java way and uses Reader/Writer classes always. Thus, JRuby engine is responsible to bridge between Reader/Writer and InputStream/OutputStream. When a Reader type object is set to ScriptEngine.eval() method, JRuby engine reads whole script and passes it to JRuby as a String. When a filename is set to a context of ScriptEngine by using ScriptEngine.FILENAME key, JRuby engine does’t use Reader object at all and creates an InputStream object based on the filename, then pass it to JRuby. This implementation is funny and has overhead but is necessary to fullfil javax.script API and fit them in JRuby. JRuby engine also wraps OutputStream since Writer type objects are required. That’s why I added WriterOutputStream class to JRuby engine.


What I wrote about here is only a part of Java and JRuby engine’s I18n mechanisms. I’ll add more later since this entry becomes pretty long.

Thursday, September 04, 2008

JRuby's unicode regular expression

In my previous post, I worte about JRuby's unicode regular expression, Joni, didn't work like Ruby's even though both engines were Oniguruma. But, the truth is ... Joni dares be off the flag that enables unicode regular expression syntax described in Oniguruma's document "since unicode tables would make jruby distribiustion a bit more boilerplate (lopex)". Lopex, who is an implementor of joni, commented on my post. I followed what lopex wrote and could get unicode regular expression run on JRuby 1.1.4 as if it is Ruby 1.9.

Here're what I did to enable the unicode flag and to get correct outputs.

1. checkout jcodings from http://svn.codehaus.org/jruby/jcodings/ because joni needs it.
2. cd jcodings; mvn clean install
3. check out joni-1_0 from http://svn.codehaus.org/jruby/joni/branches/joni-1_0/. (needs exactly this version)
4. cd joni-1_0
5. edit src/org/joni/Config.java and set true to USE_UNICODE_PROPERTIES.
6. mvn clean package
7. cp target/joni.jar <somewhere>/jruby-1.1.4/build_lib/.
8. cd <somewhere>/jruby-1.1.4
9. ant clean jar

Then, I could build customized version of JRuby, which should be unicode regular expression compliant. When I tried this UTF-8 encoded Ruby script,


p 'abcアイウαβγ'.scan(/[a-z]/)
p "abcアイウαβγ".scan(/\p{Katakana}/u)
print "abcアイウαβγ".scan(/\p{Katakana}/u), "\n\n"
p "abcアイウαβγ".scan(/\p{^Greek}/u)
print "abcアイウαβγ".scan(/\p{^Greek}/u), "\n\n"
p "abcアイウαβγ".scan(/[\u0370-\u30FF]/u)
print "abcアイウαβγ".scan(/[\u0370-\u30FF]/u), "\n"

$KCODE="utf8"
p "abcアイウαβγ".scan(/\p{Greek}/)


it printed out:


["a", "b", "c"]
["\343\202\242", "\343\202\244", "\343\202\246"]
アイウ

["a", "b", "c", "\343\202\242", "\343\202\244", "\343\202\246"]
abcアイウ

["a", "b", "c"]
abc
["α", "β", "γ"]


Although unicode codepoint from Greek to Katakana didn't work, others were good. (Ruby 1.9 showed readable characters in both p and print, but JRuby's p didn't.)
Of course, I got an error "unicode_regex.rb:2: invalid character property name {Katakana}: /\p{Katakana}/u (RegexpError)" when I tried this script by regular JRuby 1.1.4.

Following lopex's comment, I wrote this Ruby script in EUC-JP encoding and ran it on regular JRuby 1.1.4.


p "abcアイウαβγ".scan(/\p{Katakana}/e)
print "abcアイウαβγ".scan(/\p{Katakana}/e),"\n"
print "abcアイウαβγ".scan(/\p{Greek}/e),"\n"


Naturally, the last line caused an error "unicode_regexp_eucjp.rb:6: invalid character property name {Greek}: /\p{Greek}/e (RegexpError)" whatever the encoding option of regular expression was. However, two lines from the top worked and outputed:


["\245\242", "\245\244", "\245\246"]
アイウ


JRuby already has the ability to handle unicode regular expression in a Ruby way but this feature is just turned off. Since unicode regular expression is useful for non ascii language speakers, I hope this feature will trun on in near future.

Wednesday, September 03, 2008

Ruby 1.9's Unicode Regular Expression

Ruby 1.9 has greatly improved its M17N features. Unicode regular expressions would be among the most improved ones. Ruby 1.9 uses Oniguruma for its regular expression engine and enables regular expressions by unicode codepoints or property names as described in Oniguruma's document at http://www.geocities.jp/kosako3/oniguruma/doc/RE.txt.

When I tested some unicode regular expressions by ruby 1.9.0 (2008-08-26 revision 18849) [i386-darwin9.4.0], those were correctly processed. For example, this Ruby script,

# encoding: UTF-8

p 'abcアイウαβγ'.scan(/[a-z]/) # lower case alphabetical characters
p 'abcアイウαβγ'.scan(/\p{Katakana}/) # Katakana characters
p 'abcアイウαβγ'.scan(/\p{^Greek}/) # negation: other than Greek characters
p 'abcアイウαβγ'.scan(/[\u0370-\u30FF]/) # unicode codepoints from Greek to Katakana blocks

ouputs like this:

["a", "b", "c"]
["ア", "イ", "ウ"]
["a", "b", "c", "ア", "イ", "ウ"]
["ア", "イ", "ウ", "α", "β", "γ"]


The first line of Ruby script is a magic comment, which specifies an encoding of the script file. We can use either one of

# coding: UTF-8
# encoding: UTF-8
# -*- coding: UTF-8 -*-
# vim:set fileencoding=UTF-8:

to tell Ruby what encoding the script file uses. If the file starts with shebang(#!), then the magic comment goes to the second line of the file. (I found this infomation at http://i.loveruby.net/svn/rubydoc/doctree/trunk/refm/doc/spec/m17n.rd, which is written in Japanese, don't know where I can see English version of this document.)

JRuby 1.1.4 has been out there recently and started to support Ruby 1.9; however, unicode regular expressions are not included in the list. I tried to get this script run with --1.9 flag by JRuby 1.1.4, got "invalid character property name {Katakana}: /\p{Katakana}/ (RegexpError)" error. Oniguruma is also JRuby's regular expression engine like Ruby 1.9, but its implementation by Java, JONI, doesn't seem to work exactly the same as the Ruby's.

Monday, April 28, 2008

Invocable#getInterface with two interfaces

I wonder that even though multiple Java interfaces are mixed in and implemented by Ruby, whether only one invocation of getInterface method would be enough or not. To figure out what would happen, I wrote two interfaces below:
package mountainash;

public interface FlowerAttribute {
String getName();
String getColor();
double getPrice();
}

package mountainash;

public interface Flowers {
FlowerAttribute selectFlower(String name, String color);
}
Then, I wrote Ruby script implementing above interfaces like this:
# 
# Florist.rb

require 'java'

class Flower
import 'mountainash.FlowerAttribute'
def initialize(name, color, price)
@name = name
@color = color
@price = price
end
def getName
@name
end
def getColor
@color
end
def getPrice
@price
end
end

class Florist
import 'mountainash.Flowers'
def initialize
@list = [Flower.new("rose", "red", 1.99),
Flower.new("rose", "pink", 1.59),
Flower.new("tulip", "red", 0.99),
Flower.new("tulip", "pink", 1.09)]
end
def selectFlower(name, color)
@list.each { |flower| if flower.getName == name &&
flower.getColor == color
then return flower end }
end
end
Florist.new
After evaluating the script and invoking getInterface method, we will get just one mountainash.Flowers type object in Java code.
Object obj = parsedScript.eval(scriptContext);
Flowers flowers = ((Invocable)engine).getInterface(obj, Flowers.class);
Do we need to use getInterface method again to get FlowerAttribute type object, like this?
Object obj2 = flowers.selectFlower(name, color);
FlowerAttribute attribute = ((Invocable)engine).getInterface(obj2, FlowerAttribute.class);
The answer was no. In this case, just one invocation of getInterface method worked perfect.
Object obj = parsedScript.eval(scriptContext);
Flowers flowers = ((Invocable)engine).getInterface(obj, Flowers.class);
FlowerAttribute attribute = flowers.selectFlower("rose", "red");

However, I haven't tested every possible case, there might be cases getInterface method needs to be invoked multiple times. Or, older version of JRuby might have caused some trouble. I tested this on JRuby 1.1.1 and didn't have any problem.

The entire code I tried is:
package mountainash;

import java.io.*;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;
import javax.servlet.*;
import javax.servlet.http.*;

public class FloristServlet extends HttpServlet {

private ScriptEngine engine;
CompiledScript parsedScript;

@Override
public void init() {
try {
ScriptEngineManager manager = new ScriptEngineManager();
engine = manager.getEngineByName("jruby");

ClassLoader loader = getClass().getClassLoader();
InputStream iStream = loader.getResourceAsStream("ruby/Florist.rb");
Reader reader = new InputStreamReader(iStream, "UTF-8");
parsedScript = ((Compilable) engine).compile(reader);
} catch (ScriptException ex) {
Logger.getLogger(FloristServlet.class.getName()).log(Level.SEVERE, null,
ex);
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(FloristServlet.class.getName()).log(Level.SEVERE, null,
ex);
}
}

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
SimpleScriptContext scriptContext = new SimpleScriptContext();
scriptContext.setWriter(out);
try {
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet FloristServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h3>Servlet FloristServlet at " + request.getContextPath() + "</h3>");
out.println("<pre>");
Object obj = parsedScript.eval(scriptContext);
Flowers flowers = ((Invocable)engine).getInterface(obj, Flowers.class);
FlowerAttribute attribute = flowers.selectFlower("rose", "red");
out.print(getString(attribute));
out.println("<br/>");
attribute = flowers.selectFlower("rose", "pink");
out.print(getString(attribute));
out.println("<br/>");
attribute = flowers.selectFlower("tulip", "red");
out.print(getString(attribute));
out.println("<br/>");
attribute = flowers.selectFlower("tulip", "pink");
out.print(getString(attribute));
out.println("</pre>");
out.println("</body>");
out.println("</html>");
} catch (ScriptException ex) {
Logger.getLogger(FloristServlet.class.getName()).log(Level.SEVERE, null, ex);
} finally {
out.close();
}
}

private String getString(FlowerAttribute attribute) {
return attribute.getName() +
"(" + attribute.getColor() +
") : $" + attribute.getPrice();
}

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
}
Mountainash web application directory hierarchy is below:
+ Mountainash
+ WEB-INF
+ src
+ mountainash
- FloristServlet.java
- FlowerAttribute.java
- Flowers.java
+ ruby
- FLorist.rb
+ lib
- jruby.jar
- jruby-engine.jar
- script-api.jar
+ classes -+
+ mountainash | automatically created
- FloristServlet.class | directories and files
- FlowerAttribute.class | by IDE
- Flowers.class |
+ ruby |
- Florist.rb -+
Correct output from FloristServlet will be:
<html>
<head>
<title>Servlet FloristServlet</title>
</head>
<body>
<h3>Servlet FloristServlet at /Mountainash</h3>
<pre>
rose(red) : $1.99<br/>
rose(pink) : $1.59<br/>
tulip(red) : $0.99<br/>
tulip(pink) : $1.09</pre>
</body>
</html>

Friday, April 18, 2008

JRuby engine for JDK 1.5

I've released JSR 223 JRuby engine for JDK 1.5 the other day. New release includes JRubyScirptEngineManager class, which is alternative to javax.script.ScriptEngineManager for JDK 1.5 users. However, newly added class was not necessary for JDK 1.5 users. NetBeans tricked me.

When I tested JRuby engine on JDK 1.5, I could not avoid version mismatch error caused by some archives compiled on JDK 1.6. I thought this must have come from script-api.jar which is distributed in a binary form. The archive was the only one that considerably compiled on JDK 1.6. JAVA_HOME was set to 1.5, commend, java -version, ouputs version 1.5 correctly, etc/netbeans.conf has correct path to JDK 1.5, and the project on NetBeans has 1.5 library only. However, script-api.jar was not a culprit. It was NetBeans trick. NetBeans has cached old classpath to 1.6 of the project and never refreshes it. Caches are in directories under /Users/yoko/.netbeans/6.1beta/var/cache/index/0.8. My JDK 1.6 for Mac OSX has JSR 223 AppleScript engine in it and causes version mismatch error. Even though NetBeans works on JDK 1.5, and the project has JDK 1.5 library only, cached classpath to JDK 1.6 is vaild for execution. Very tricky. The old cached classpath is not valid for compile, so I got compile error if I did not have script-api.jar in the project library.

Although JRubyScriptEngineManager does not need for JDK 1.5, this class helped me to figure out what's going on. Moreover, I added relevant exception which would help users keep from stucking into. javax.script.ScriptEngineManager does not raise any exception even though it could not find script engine. People often don't know null is returned and got exception at the eval() method line. JRubyScriptEngineManager might help users out.

Monday, April 07, 2008

Tips for JRuby engine: Sites to learn about scripting API

For the eighth post of "Tips for JRuby engine" series I've written in this blog, I'll introduce useful sites to learn about JSR 223 scripting API.

Tips for JRuby engine: Compilable and CompiledScript usage

This is the seventh post of "Tips for JRuby engine" series I've written in this blog and shows how to use Compilable interface and CompiledScript class defined in JSR 223 scripting API, and why they are useful. These two APIs are used when programmers want to just parse scripts without evaluating them. Once the script has been parsed, programmers can evaluate it continuously as many times as they need without parsing. This mechnism is useful especially for web applications because we don't want to parse scripts for every http request after debugging has completed. We want to just evaluate them to process each http request. When scripting and Servlet APIs are put together, we could receive benefit of Compilable and CompiledScript APIs fully.

Let's look at an example program:
package mountainash;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CompilableTestServlet extends HttpServlet {

private ScriptEngine engine;
CompiledScript parsedScript, parsedScript2;

@Override
public void init() {
try {
ScriptEngineManager manager = new ScriptEngineManager();
engine = manager.getEngineByName("jruby");

ClassLoader loader = getClass().getClassLoader();
InputStream iStream = loader.getResourceAsStream("ruby/simple2.rb");
Reader reader = new InputStreamReader(iStream, "UTF-8");
parsedScript = ((Compilable) engine).compile(reader);

iStream = loader.getResourceAsStream("ruby/simple3.rb");
reader = new InputStreamReader(iStream, "UTF-8");
parsedScript2 = ((Compilable) engine).compile(reader);
} catch (ScriptException ex) {
Logger.getLogger(CompilableTestServlet.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(CompilableTestServlet.class.getName()).log(Level.SEVERE, null, ex);
}
}

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
SimpleScriptContext scriptContext = new SimpleScriptContext();
scriptContext.setWriter(out);

try {
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet CompilableTestServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h3>Servlet CompilableTestServlet at " + request.getContextPath() + "</h3>");
out.println("<pre>");
parsedScript.eval(scriptContext);
out.println("<br/>");
parsedScript2.eval(scriptContext);
out.println("</pre>");
out.println("</body>");
out.println("</html>");
} catch (ScriptException ex) {
Logger.getLogger(CompilableTestServlet.class.getName()).log(Level.SEVERE, null, ex);
} finally {
out.close();
}
}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
public String getServletInfo() {
return "JRuby engine test: use Compilable interface";
}
}

--------------------

# simple2.rb

puts "Compilable interface test"
puts "できたかな?"

--------------------

# simple3.rb

def greetings(to)
puts "Good morning, #{to}!"
end

greetings("glassfish")
greetings("grasshopper")
greetings("みなさん")
This servlet parses two Ruby scripts in its init() method, then, evaluated in its processRequest() method. This means that two scripts are parsed only once when the servlet is loaded onto a servlet container and are just evaluated everytime http request occurs. Two APIs, Compilable and CompiledScript, would contribute to improve performance of web applications. The busier the web application is, the more we can expect performance improvement because processing time for parsing is eliminated.

To try this example, please see my former post to know bases.

Here's a web application directory hierarchy:

+ Mountainash
+ WEB-INF
+ src
+ mountainash
- SimpleServlet.java
- CompilableTestServlet.java
+ ruby
- simple.rb
- simple2.rb
- simple.rb
+ lib
- jruby.jar
- jruby-engine.jar
+ classes -+
+ mountainash | automatically created
- SimpleServlet.class | directories and files
- CompilableTestSErvlet.class | by IDE
+ ruby |
- simple.rb |
- simple2.rb |
- simple3.rb -+
When this servlet gets run successfully, we get the following output:
<html>
<head>
<title>Servlet CompilableTestServlet</title>
</head>
<body>
<h3>Servlet CompilableTestServlet at /Mountainash</h3>
<pre>
Compilable interface test
できたかな?
<br/>
Good morning, glassfish!
Good morning, grasshopper!
Good morning, みなさん!
</pre>
</body>
</html>

Well, the names, Compilable and CompiledScript, are confusing for JRuby users. JRuby has a feature to compile Ruby script to create Java bytecode. Perhaps, Parsable and ParsedScript would be more understandable names.

Saturday, April 05, 2008

Tips for JRuby engine: servlet meets JRuby engine

This is the sixth post of "Tips for JRuby engine" series I've written in this blog and shows how to get started writing servlet code using JSR 223 scripting API. Scripting API are smoothly mixed into other APIs since it is a part of standard Java APIs. Using JSR 223 scripting and Servlet APIs, we can make web applications more easily and delightfully thanks to the power of dynamic languages. However, programmers should be careful to use classes an interfaces of scripting API. Scripting API considers to be run on a servlet though not perfect, while implementations of dynamic languages often have some flaws for servlet style concurrent programming. Once scripting API works togther with Servlet API, multiple threads execute a single method of a single instance concurrently. How about JRuby implementation? It is not basicaly compliant to the servlet style, concurrent invocation. For example, JRuby's runtime has only one instance of GlobalVariables which is used to share instances between Java applications and JRuby and might cause a race condition when it executes consecutive mutilple evaluations on its single runtime. Thus, I'll write about what programers should take into account to write a servlet code effectively, a couple of times in this blog.

Firstly, I'll start with a very simple servlet. It just shows simple messages after executing a hello-world script written in Ruby. Tediously simple, but important to make sure that scripting APIs work on a servlet container. Because Servlet API and its web application require a specific directory structure, and adopt their own classloading mechanism, we need to fit scripting API in to a servlet based web application. Unless, some of programmers might experience hard time for the frist servlet code in action.

Before start writing the code, we must set up two jar archives, jruby.jar and jruby-engine.jar, to the right place. It is WEB-INF/lib. Most programmers use some sort of IDE and probably know about how to set up these two archives in a web application. I'm a NetBeans user, so I clicked on Libraries > Add JAR/Folder menu of a web project, and added them.

Here are the simple servlet and Ruby script:
package mountainash;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import java.io.Reader;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleServlet extends HttpServlet {

private ScriptEngine engine;

@Override
public void init() {
ScriptEngineManager manager = new ScriptEngineManager();
engine = manager.getEngineByName("jruby");

}

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
SimpleScriptContext scriptContext = new SimpleScriptContext();
scriptContext.setWriter(out);

try {
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet SimpleServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h3>Servlet SimpleServlet at " + request.getContextPath() + "</h3>");
out.println("<pre>");
ClassLoader loader = getClass().getClassLoader();
Reader reader =
new InputStreamReader(loader.getResourceAsStream("ruby/simple.rb"), "UTF-8");
engine.eval(reader, scriptContext);

out.println("</pre>");
out.println("</body>");
out.println("</html>");
} catch (ScriptException ex) {
Logger.getLogger(SimpleServlet.class.getName()).log(Level.SEVERE, null, ex);
} finally {
out.close();
}
}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
public String getServletInfo() {
return "JRuby Engine Test: very simple";
}
}

--------------------

# simple.rb

puts "Hello World from Ruby over JRuby engine"
puts "こんにちは世界"
Lines colored red have scripting APIs mixed in to this servlet. In a init() method, the servlet gets JRuby engine's instance, which means that this servlet has only one JRuby runtime to process multiple requests rushed to this servlet all at once. Also, only one instance of JRuby engine takes care of multiple http requests concurrently. This is why this servlet doesn't use engine.put() or engine.setWriter() methods not to be suffered from a race condition. Instead, this servlet creates SimpleScriptContext type instance for each http request to share objects with Ruby script.

Where do I put Ruby scripts? This question might come up since everything should be in Java's web application directory structure. The way of getting scripts depends on where are they. We can take scripts in the servlet either specifing the file path relative to the web application's context root or reading the file as a resource by using a class loader. This servlet uses the latter one, so Ruby scripts must be in the WEB-INF/lib or WEB-INF/classes directory, or subdirectories of WEB-INF/classes, which are available for web application's class loader to load resources. Again, I'm a NetBeans user, so I created the folder whose name is ruby under the Source Packages folder and put the script there. Then, NetBeans copied it to build/web/WEB-INF/classes/ruby directory automatically, consequently, the script became loadable.
Mountainash project's directory hierarcy

+ Mountainash
+ WEB-INF
+ src
+ mountainash
- SimpleServlet.java
+ ruby
- simple.rb
+ lib
- jruby.jar
- jruby-engine.jar
+ classes -+
+ mountainash | automatically created
- SimpleServlet.class | directories and files
+ ruby |
- simple.rb -+

When this servlet gets run successfully, we get the following output:
<html>
<head>
<title>Servlet SimpleServlet</title>
</head>
<body>
<h3>Servlet SimpleServlet at /Mountainash</h3>
<pre>
Hello World from Ruby over JRuby engine
こんにちは世界
</pre>
</body>
</html>

Tuesday, April 01, 2008

Tips for JRuby engine: getInterface() usage

This is the fifth post of "Tips for JRuby engine" series I've written in this blog and shows how to use javax.script.Invocable#getInterface() method defined in JSR 223 APIs. This method is used when Ruby scripts provide implementations of Java defined interfaces. In other words, JSR 223 API allows us to write interfaces only in Java and implement them by Ruby. A programmer can discover interface-implemented, Ruby-created instances by using getInterface() method and invoke methods defined in the interfaces.

JSR 223 API defines two types of getInterface() method - <T> T getInterface(Class<T> clasz), and <T> T getInterface(Object thiz, Class<T> clasz). The difference of these two methods is similar to the one between invokeFunction() and invokeMethod(). While the former method is applied to the top-level methods, the latter is done to the methods defined in classes or modules. Likewise, <T> T getInterface(Class<T> clasz) is used when interface-defined methods are implemented by Ruby's top-level methods, and <T> T getInterface(Object thiz, Class<T> clasz) is for methods that Ruby scripts implemented in classes or modules.

If programmers want to implement Java interface by Ruby, they need at least two statements in Ruby script - first, require 'java': second, include_class for top-level methods, or import for others to claim that the script is the implementation of some interfaces. Suppose the interface below has defined:
package canna;

import java.util.List;

public interface SimpleFile {
void create(String filename);
void write(List list);
void close();
}
When the programmers implement this interface by Ruby's top-level methods, Ruby script would be like this:
require 'java'
include_class 'canna.SimpleFile'
def create(name)
@name = name;
@tmpfile = File.new(name, "w");
@tmpfile.chmod(0600);
end
def write(message)
message.each { |m| @tmpfile.puts(m) }
end
def close()
@tmpfile.close;
puts "The file, #{@name}, has #{File.size(@name)} bytes."
end
Thie script has require 'java' and include_class 'canna.SimpleFile' statements in the beginning to load Java extension and to include Java interface, then defines create, write, and close methods that the interface, canna.SimpleFile, has. In case of top-level methods' implementation, script doesn't need to return any instance since JRuby engine gets a receiver object, "self," from JRuby's runtime internally. Utilizing Ruby's implementation, Java code would be:
engine.eval(script);
Invocable invocable = (Invocable) engine;
SimpleFile simpleFile = invocable.getInterface(SimpleFile.class);
simpleFile.create("simplefile.txt");
List list = new ArrayList();
list.add("A bird in the hand is worth two in the bush.");
list.add("Birds of a feather flock together.");
list.add("Every bird loves to hear himself sing.");
simpleFile.write(list);
simpleFile.close();

Once, we run this code, we'll find the file whose name is simlefile.txt in file system and see three lines in it. As we expect, simplfile.txt has the mode 0600:
-rw-------   1 yoko  staff   119  4  2 14:06 simplefile.txt

How should the programmers write a code if they want to implemment Java interfaces by defiening Ruby classes? The Ruby script must have require 'java' statement prior to a class definition, and import 'canna.SimpleFile' statement in a class definition. Besides, the script must return more than one instance. Thus, it would look like:
require 'java'
class SimpleFileImple
import 'canna.SimpleFile'
def initialize(name)
@name = name;
@tmpfile = File.new(name, "w");
@tmpfile.chmod(0600)
end
def write(message)
message.each { |m| @tmpfile.puts(m) }
end
def close()
@tmpfile.close;
puts "The file, #{@name}, has #{File.size(@name)} bytes."
end
end
SimpleFileImple.new($name)
SimpleFileImple class implements Java-defined canna.SimpleFile interface, and has a constructor, and write and close methods definition, but not create method. Ruby doesn't complain that not all methods of the interface imported are covered. Then, Java code would be written below:
engine.put("name", "simplefile2.txt");
Object object = engine.eval(script);
Invocable invocable = (Invocable) engine;
SimpleFile simpleFile = invocable.getInterface(object, SimpleFile.class);
List list = new ArrayList();
list.add("When it is a question of money, everybody is of the same religion.");
list.add("Money is the wise man's religion.");
simpleFile.write(list);
simpleFile.close();
This code gets java.lang.Object typed instance when the Ruby script is evaluated. This instance must be created by Ruby and is able to be casted into canna.SimpleFile type, or getInterface() method doesn't work correctly. The argument of the constructor, which is a file name, is passed by using a global variable name, so simplefile2.txt will appear after the code gets run.

At last, I'll demonstrate more complicated but more real example. In a real application, people often implement more than one interface in a single class, and instantiate mutiple objects based on the single definition. To see how to do this, let's create very simple two interfaces, canna.Remarkable and canna.Removable, shown below:
package canna;

public interface Remarkable {
void remark();
}

package canna;

public interface Removable {
void remove(int i);
}
Then, think about what the Ruby script would be. We need to have require 'java' and two import statements in it, plus, methods' implementations; moreover, we are going to create and return two instances to give over to Java. Considering these requirements, we would write following Ruby script to wrap them up.
require 'java'
class Flowers
import 'canna.Remarkable'
import 'canna.Removable'
@@hash = {'red' => 'ruby', 'white' => 'pearl'}
def initialize(color, names)
@color = color;
@names = names;
end
def remark
puts "#{@names.join(', ')}. Beautiful like a #{@@hash[@color]}!"
end
def remove(index)
print "If I remove #{@names[index]}, ";
@names.delete_at(index);
print "others will be #{@names.join(', ')}."
end
end
red = Flowers.new("red", ["cameliia", "hibiscus", "rose", "canna"])
white = Flowers.new("white", ["gardenia", "lily", "magnolia"])
return red, white
When this script is evaluated, we'll get a java.util.List type object that has multiple return values in its elements. Therefore, in Java code, we would take each instance out from the List type object, and invoke methods defined in the interfaces, like this:
Object objects = engine.eval(script);
Invocable invocable = (Invocable) engine;
if (objects instanceof List) {
for (Object object : (List)objects) {
Object flower = invocable.getInterface(object, Remarkable.class);
((Remarkable)flower).remark();
flower = invocable.getInterface(object, Removable.class);
((Removable)flower).remove(1);
}
}


This is an interesting feature of JSR 223 APIs; however, old versions of JRuby had a problem to execute getInterface() method correctly. Make sure your JRuby is 1.1RC2 or later.

Here are entire codes to perform all snippets I mentioned here:
//Remarkable.java
package canna;

public interface Remarkable {
void remark();
}

//Removable.java
package canna;

public interface Removable {
void remove(int i);
}

//GetInterfacesExample.java
package canna;

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

public class GetInterfacesExample {
private GetInterfacesExample() throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
getInterfaceByTopLevel(engine);
getInterfaceByClass(engine);
getInterfaceMultipleStuffs(engine);
}

private void getInterfaceByTopLevel(ScriptEngine engine) throws ScriptException {
String script =
"require \'java\'\n" +
"include_class \'canna.SimpleFile\'\n" +
"def create(name)" +
"@name = name;" +
"@tmpfile = File.new(name, \"w\");" +
"@tmpfile.chmod(0600);" +
"end\n" +
"def write(message)" +
"message.each { |m| @tmpfile.puts(m) }" +
"end\n" +
"def close()" +
"@tmpfile.close;" +
"puts \"The file, #{@name}, has #{File.size(@name)} bytes.\"" +
"end";

engine.eval(script);
Invocable invocable = (Invocable) engine;
SimpleFile simpleFile = invocable.getInterface(SimpleFile.class);
simpleFile.create("simplefile.txt");
List list = new ArrayList();
list.add("A bird in the hand is worth two in the bush.");
list.add("Birds of a feather flock together.");
list.add("Every bird loves to hear himself sing.");
simpleFile.write(list);
simpleFile.close();
}

private void getInterfaceByClass(ScriptEngine engine) throws ScriptException {
String script =
"class SimpleFileImple\n" +
"import \'canna.SimpleFile\'\n" +
"def initialize(name)" +
"@name = name;" +
"@tmpfile = File.new(name, \"w\");" +
"@tmpfile.chmod(0600)" +
"end\n" +
"def write(message)" +
"message.each { |m| @tmpfile.puts(m) }" +
"end\n" +
"def close()" +
"@tmpfile.close;" +
"puts \"The file, #{@name}, has #{File.size(@name)} bytes.\"" +
"end\n" +
"end\n" +
"SimpleFileImple.new($name)";

engine.put("name", "simplefile2.txt");
Object object = engine.eval(script);
Invocable invocable = (Invocable) engine;
SimpleFile simpleFile = invocable.getInterface(object, SimpleFile.class);
List list = new ArrayList();
list.add("When it is a question of money, everybody is of the same religion.");
list.add("Money is the wise man's religion.");
simpleFile.write(list);
simpleFile.close();
}

private void getInterfaceMultipleStuffs(ScriptEngine engine) throws ScriptException {
String script =
"class Flowers\n" +
"import \'canna.Remarkable\'\n" +
"import \'canna.Removable\'\n" +
"@@hash = {\'red\' => \'ruby\', \'white\' => \'pearl\'}\n" +
"def initialize(color, names)" +
"@color = color;" +
"@names = names;" +
"end\n" +
"def remark\n" +
"puts \"#{@names.join(\', \')}. Beautiful like a #{@@hash[@color]}!\"" +
"end\n" +
"def remove(index)" +
"print \"If I remove #{@names[index]}, \";" +
"@names.delete_at(index);" +
"print \"others will be #{@names.join(\', \')}.\n\"" +
"end\n" +
"end\n" +
"red = Flowers.new(\"red\", [\"cameliia\", \"hibiscus\", \"rose\", \"canna\"])\n" +
"white = Flowers.new(\"white\", [\"gardenia\", \"lily\", \"magnolia\"])\n" +
"return red, white";

Object objects = engine.eval(script);
Invocable invocable = (Invocable) engine;
if (objects instanceof List) {
for (Object object : (List)objects) {
Object flower = invocable.getInterface(object, Remarkable.class);
((Remarkable)flower).remark();
flower = invocable.getInterface(object, Removable.class);
((Removable)flower).remove(1);
}
}
}

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

In above code, the second and third script don't have require 'java' statement because the first script has loaded java extension onto JRuby runtime and is valid until the program exits.

If these code gets run successfully, they produce outputs below:
he file, simplefile.txt, has 119 bytes.
The file, simplefile2.txt, has 101 bytes.
cameliia, hibiscus, rose, canna. Beautiful like a ruby!
If I remove hibiscus, others will be cameliia, rose, canna.
gardenia, lily, magnolia. Beautiful like a pearl!
If I remove lily, others will be gardenia, magnolia.
In addition, two files will be created on file system and have the mode 0600:
-rw-------   1 yoko  staff   119  4  2 14:06 simplefile.txt
-rw------- 1 yoko staff 101 4 2 14:06 simplefile2.txt

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.