org.jruby.embed.ScriptingContainer Maven / Gradle / Ivy
/**
* **** BEGIN LICENSE BLOCK *****
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Common Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/cpl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2009-2011 Yoko Harada
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the CPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the CPL, the GPL or the LGPL.
* **** END LICENSE BLOCK *****
*/
package org.jruby.embed;
import java.io.UnsupportedEncodingException;
import org.jruby.embed.internal.LocalContextProvider;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jruby.CompatVersion;
import org.jruby.Profile;
import org.jruby.Ruby;
import org.jruby.RubyGlobal.InputGlobalVariable;
import org.jruby.RubyGlobal.OutputGlobalVariable;
import org.jruby.RubyIO;
import org.jruby.RubyInstanceConfig.CompileMode;
import org.jruby.RubyInstanceConfig.LoadServiceCreator;
import org.jruby.RubyInstanceConfig.ProfilingMode;
import org.jruby.embed.internal.BiVariableMap;
import org.jruby.embed.internal.ConcurrentLocalContextProvider;
import org.jruby.embed.internal.EmbedRubyInterfaceAdapterImpl;
import org.jruby.embed.internal.EmbedRubyObjectAdapterImpl;
import org.jruby.embed.internal.EmbedRubyRuntimeAdapterImpl;
import org.jruby.embed.internal.SingleThreadLocalContextProvider;
import org.jruby.embed.internal.SingletonLocalContextProvider;
import org.jruby.embed.internal.ThreadSafeLocalContextProvider;
import org.jruby.embed.io.ReaderInputStream;
import org.jruby.embed.io.WriterOutputStream;
import org.jruby.embed.util.SystemPropertyCatcher;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.Block;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ClassCache;
import org.jruby.util.KCode;
/**
* ScipritngContainer provides various methods and resources that are useful
* for embedding Ruby in Java. Using this class, users can run Ruby scripts from
* Java programs easily. Also, users can use methods defined or implemented by Ruby.
*
* ScriptingContainer allows users to set configuration parameters, which are per-container
* properties and per-evaluation attributes. For example, a local context scope,
* local variable behavior, load paths are per-container properties. Please see
* {@link PropertyName} and {@link AttributeName} for more details.
* The per-container properties should be given prior to the Ruby runtime is
* instantiated; otherwise, default values are applied to. SCriptingContainer delays
* Ruby runtime initialization as much as possible to improve startup time. When
* some values are put into the ScriptingContainer, or runScriptlet method is used,
* Ruby runtime is created internally.
*
* Below are examples.
*
* The first Example is a very simple Hello World. After initializing a ScriptingContainer,
* a Ruby script, puts "Hello World!", runs and produces "Hello World!."
* Example 1:
*
* ScriptingContainer container = new ScriptingContainer();
* container.runScriptlet("puts \"Hello World!\"");
*
* Produces:
* Hello World!
*
* The second example shows how to share variables between Java and Ruby.
* In this example, a local variable "x" is shared. To make this happen, a local variable
* behavior should be transient or persistent. As for JSR223 JRuby engine, set these
* types using System property, org.jruby.embed.localvariable.behavior. If the local
* variable behavior is one of transient or persistent,
* Ruby's local, instance, global variables and constants are available to share
* between Java and Ruby. (A class variable sharing does not work on current version)
* Thus, "x" in Java is also "x" in Ruby.
*
* Example 2:
*
* ScriptingContainer container = new ScriptingContainer();
* container.put("x", 12345);
* container.runScriptlet("puts x.to_s(2)");
*
* Produces:
* 11000000111001
*
* The third examples shows how to keep local variables across multiple evaluations.
* This feature simulates BSF engine for JRuby. In terms of Ruby semantics,
* local variables should not survive after the evaluation has completed. Thus,
* this behavior is optional, and users need to specify LocalVariableBehvior.PERSISTENT
* when the container is instantiated.
*
* Example 3:
*
* ScriptingContainer container = new ScriptingContainer(LocalVariableBehavior.PERSISTENT);
* container.runScriptlet("p=9.0");
* container.runScriptlet("q = Math.sqrt p");
* container.runScriptlet("puts \"square root of #{p} is #{q}\"");
* System.out.println("Ruby used values: p = " + container.get("p") +
* ", q = " + container.get("q"));
*
* Produces:
* square root of 9.0 is 3.0
* Ruby used values: p = 9.0, q = 3.0
*
* Also, ScriptingContainer provides better i18n support. For example,
* Unicode Escape Sequence can be included in Ruby scripts.
*
* In addition, ScriptingContainer supports a parse-once-eval-many-times feature,
* invoking methods defined by Ruby, and getting an instance of a specified interface
* that has been implemented by Ruby.
*
*
Example 4:
* ScriptingContainer container = new ScriptingContainer();
* script =
* "def message\n" +
* "\"message: #{@message}\"\n" +
* "end\n" +
* "message";
* container.put("@message", "What's up?");
* EvalUnit unit = container.parse(script);
* IRubyObject ret = unit.run();
* System.out.println(JavaEmbedUtils.rubyToJava(ret));
* container.put("@message", "Fabulous!");
* ret = unit.run();
* System.out.println(JavaEmbedUtils.rubyToJava(ret));
* container.put("@message", "That's the way you are.");
* ret = unit.run();
* System.out.println(JavaEmbedUtils.rubyToJava(ret));
*
* Produces:
* message: What's up?
* message: Fabulous!
* message: That's the way you are.
*
* See more details at project's
* {@see Wiki}
*
* @author Yoko Harada
*/
public class ScriptingContainer implements EmbedRubyInstanceConfigAdapter {
private Map basicProperties = null;
private LocalContextProvider provider = null;
private EmbedRubyRuntimeAdapter runtimeAdapter = new EmbedRubyRuntimeAdapterImpl(this);
private EmbedRubyObjectAdapter objectAdapter = new EmbedRubyObjectAdapterImpl(this);
private EmbedRubyInterfaceAdapter interfaceAdapter = new EmbedRubyInterfaceAdapterImpl(this);
/**
* Constructs a ScriptingContainer with a default values.
*/
public ScriptingContainer() {
this(LocalContextScope.SINGLETON, LocalVariableBehavior.TRANSIENT, true);
}
/**
* Constructs a ScriptingContainer with a specified local context type.
*
* @param scope a local context type.
*/
public ScriptingContainer(LocalContextScope scope) {
this(scope, LocalVariableBehavior.TRANSIENT, true);
}
/**
* Constructs a ScriptingCOntainer with a specified local variable behavior.
*
* @param behavior a local variable behavior
*/
public ScriptingContainer(LocalVariableBehavior behavior) {
this(LocalContextScope.SINGLETON, behavior, true);
}
/**
* Constructs a ScriptingContainer with a specified local context type and
* variable behavior.
*
* @param scopea a local context type
* @param behavior a local variable behavior
*/
public ScriptingContainer(LocalContextScope scope, LocalVariableBehavior behavior) {
this(scope, behavior, true);
}
/**
* Constructs a ScriptingContainer with a specified local context scope,
* local variable behavior and laziness.
*
* @param scope is one of a local context scope defined by {@link LocalContextScope}
* @param behavior is one of a local variable behavior defined by {@link LocalVariableBehavior}
* @param lazy is a switch to do lazy retrieval of variabes/constants from
* Ruby runtime. Default is true. When this value is true, ScriptingContainer tries to
* get as many variables/constants as possible from Ruby runtime.
*/
public ScriptingContainer(LocalContextScope scope, LocalVariableBehavior behavior, boolean lazy) {
provider = getProviderInstance(scope, behavior, lazy);
try {
initConfig();
} catch (Exception ex) {
Writer w = getErrorWriter();
ex.printStackTrace((PrintWriter)w);
throw new RuntimeException(ex);
}
setBasicProperties();
}
private LocalContextProvider getProviderInstance(LocalContextScope scope, LocalVariableBehavior behavior, boolean lazy) {
switch(scope) {
case THREADSAFE :
return new ThreadSafeLocalContextProvider(behavior, lazy);
case CONCURRENT :
return new ConcurrentLocalContextProvider(behavior, lazy);
case SINGLETHREAD :
return new SingleThreadLocalContextProvider(behavior, lazy);
case SINGLETON :
default :
LocalVariableBehavior b = SingletonLocalContextProvider.getLocalVariableBehaviorOrNull();
if (b == null) return new SingletonLocalContextProvider(behavior, lazy);
else return new SingletonLocalContextProvider(b, lazy);
}
}
private void initConfig() throws URISyntaxException, UnsupportedEncodingException {
List paths = SystemPropertyCatcher.findLoadPaths();
provider.getRubyInstanceConfig().setLoadPaths(paths);
String home = SystemPropertyCatcher.findJRubyHome(this);
if (home != null) {
provider.getRubyInstanceConfig().setJRubyHome(home);
}
provider.getRubyInstanceConfig().setCompileMode(CompileMode.OFF);
provider.getRubyInstanceConfig().setScriptFileName("