/** * This set of classes and methods explains how the JSR-223 Scripting API can * allow the scripting engine to implement Java interfaces. In this example, * Yoko Harada shows how you can create interfaces in Java which will be * implemented by the JRuby runtime. See the Remarkable.java, Removable.java, * and SimpleFile.java interfaces for the details. * * Note: Minor changes were made to this class by means of specifying the alternative * package namespace that SimpleFile or Removable and Remarkable are declared in. * canna to klauer.callingruby.yokoharada * @author Yoko Harada * See: http://yokolet.blogspot.com/2008/04/tips-for-jruby-engine-getinterface.html */ package klauer.callingruby.yokoharada; 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 { /** * To run these examples, the constructo first creates the JRuby engine, * and then calls each of these examples. * * @throws javax.script.ScriptException */ private GetInterfacesExample() throws ScriptException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("jruby"); getInterfaceByTopLevel(engine); getInterfaceByClass(engine); getInterfaceMultipleStuffs(engine); } /** * The most basic way of implementing a Java Interface is to call the two * lines--require 'java' and include_class <interface you want to implement> * From here it is a matter of defining what those interfaces specified you do. * * You need not worry about returning an instnace of the script to Java, since * the engine gets a receiver of 'self' back from the JRuby runtime itself. * * In Java, you have to cas * What this example does is implement a very simple interface, SimpleFile.java, * that has three methods to it: create(), write(message), and close(). * * @param engine * @throws javax.script.ScriptException */ private void getInterfaceByTopLevel(ScriptEngine engine) throws ScriptException { String script = "require \'java\'\n" + "include_class \'klauer.callingruby.yokoharada.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(); } /** * If we want to implement a Java interface that exists inside of a Ruby * class, we change our two initial method calls in JRuby to: * require 'java' * class <class you've defined> * import <interface you want to implement> * and define your methods in that interface, <b>inside</b> of a class. * * All other steps are the same as before. * @param engine * @throws javax.script.ScriptException */ private void getInterfaceByClass(ScriptEngine engine) throws ScriptException { String script = "class SimpleFileImple\n" + "import \'klauer.callingruby.yokoharada.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(); } /** * Slightly different than the two methods * {@link GetInterfacesExample#getInterfaceByTopLevel(javax.script.ScriptEngine) } * and {@link GetInterfacesExample#getInterfaceByClass(javax.script.ScriptEngine) }, * this example implements multiple Interfaces. To do this, we import both of the * interface classes and implement their members. * * In Java, we would get an instance of a java.util.List returned to us that * we can cast the java.lang.Object to, using it's implemented members. * @param engine * @throws javax.script.ScriptException */ private void getInterfaceMultipleStuffs(ScriptEngine engine) throws ScriptException { String script = "class Flowers\n" + "import \'klauer.callingruby.yokoharada.Remarkable\'\n" + "import \'klauer.callingruby.yokoharada.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(); } }