001/* An actor whose execution is specified by a JRuby script. 002 003Copyright (c) 2013 The Regents of the University of California. 004All rights reserved. 005Permission is hereby granted, without written agreement and without 006license or royalty fees, to use, copy, modify, and distribute this 007software and its documentation for any purpose, provided that the above 008copyright notice and the following two paragraphs appear in all copies 009of this software. 010 011IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015SUCH DAMAGE. 016 017THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022ENHANCEMENTS, OR MODIFICATIONS. 023 024*/ 025package org.kepler.scriptengine; 026 027import javax.script.ScriptException; 028 029import ptolemy.kernel.CompositeEntity; 030import ptolemy.kernel.util.IllegalActionException; 031import ptolemy.kernel.util.NameDuplicationException; 032import ptolemy.kernel.util.Workspace; 033 034/** An actor whose execution is defined by a JRuby script. 035 * The script can be edited by double-clicking on the actor. 036 * 037 * <p><b>NOTE:</b>The JRuby jar must be downloaded separately 038 * since it is released with the GPL license.</p> 039 * 040 * <p>The following example defines an actor in JRuby that 041 * computes factorials:</p> 042<pre> 0431. include Java 0442. 0453. import Java::ptolemy.data.IntToken 0464. 0475. class Actor 0486. # create accessor methods for self field 0497. # the java actor will be assigned to this field. 0508. attr_accessor :self 0519. 05210. def fire 05311. # read the input value from the port "input" 05412. val = @self.getPort('input').get(0).intValue 05513. 05614. # calculate the factorial 05715. total = 1 05816. if val < 0 05917. @self.error("Input must be greater than or equal to 0") 06018. else 06119. while val > 1 do 06220. total *= val 06321. val -= 1 06422. end 06523. end 06624. 06725. # write the factorial to the port "output" 06826. @self.getPort('output').broadcast(IntToken.new(total)) 06927. end 07028. end 071</pre> 072 * 073 * <p>Line 1 includes the module for the JVM, and line 3 imports the Java 074 * class used by the actor. Lines 5-28 define the Actor object; the actor 075 * object must be named "Actor" in JRuby scripts. Line 8 specifies that 076 * accessor methods should be created for a field called "self". This 077 * field is a reference to the Java object of this actor, and can be used 078 * to access ports and parameters. Lines 10-27 define the fire() method of 079 * the actor, which is called each time this actor executes in the workflow. 080 * Line 12 reads an integer from the input port called "input". A port or 081 * parameter called "foo" can be accessed in the JRuby script by using 082 * @self.getPort("foo") or @self.attribute("foo"), respectively. Lines 15-23 083 * compute the factorial of the input number, and line 26 writes the result 084 * to the output port called "output".</p> 085 * 086 * @author Daniel Crawl 087 * @version $Id: JRuby.java 33884 2015-09-11 18:07:23Z crawl $ 088 */ 089public class JRuby extends ScriptEngineActor { 090 091 /** Construct a new JRuby for a specified workspace. */ 092 public JRuby(Workspace workspace) { 093 super(workspace); 094 } 095 096 /** Construct a new JRuby with the given container and name. */ 097 public JRuby(CompositeEntity container, String name) 098 throws IllegalActionException, NameDuplicationException { 099 super(container, name); 100 101 // this property must be set to persistent so that assignment 102 // statements in the script are persistent across calls to 103 // engine.eval() and engine.put(). 104 System.setProperty("org.jruby.embed.localvariable.behavior", "persistent"); 105 106 try { 107 language.setToken("ruby"); 108 } catch(IllegalActionException e) { 109 // an exception is probably due to the jruby jar missing 110 throw new IllegalActionException(this, e, 111 "Error loading the script engine for JRuby. This can happen if\n" + 112 "the JRuby jar is not present. This jar is not included with\n" + 113 "Kepler due to licensing and must be downloaded separately.\n" + 114 "To use the JRuby actor: download the jar, either add it to\n" + 115 "your $CLASSPATH environment variable or copy it into\n" + 116 "kepler.modules/actors/lib/jar/, and restart Kepler."); 117 } 118 119 _editorFactory.syntaxStyle.setExpression("text/ruby"); 120 121 // a simple actor that prints when fire() is called 122 script.setExpression("class Actor\n" + 123 " attr_accessor :self\n" + 124 " def fire\n" + 125 " puts \"in fire\"\n" + 126 " end\n" + 127 "end\n"); 128 } 129 130 /////////////////////////////////////////////////////////////////// 131 //// protected methods //// 132 133 /** Create an instance of the actor object in the script. 134 * @return the actor instance. 135 */ 136 @Override 137 protected Object _createActorInstance(String actorClassNameStr) throws ScriptException { 138 _engine.eval(_ACTOR_INSTANCE_NAME + " = " + actorClassNameStr + ".new"); 139 return _engine.get(_ACTOR_INSTANCE_NAME); 140 } 141 142 /** Put the given object to the actor instance in the script. 143 * @param name the name of the object to put. 144 * @param object the object to put. 145 */ 146 @Override 147 protected void _putObjectToActorInstance(String name, Object object) throws ScriptException { 148 String globalName = "_yyy_" + name; 149 _engine.put(globalName, this); 150 _engine.eval(_ACTOR_INSTANCE_NAME + ".self = " + globalName); 151 } 152}