All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jruby.embed.ScriptingContainer Maven / Gradle / Ivy

There is a newer version: 9.4.9.0
Show newest version
/**
 * **** BEGIN LICENSE BLOCK *****
 * Version: EPL 1.0/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Eclipse 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/epl-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-2013 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 EPL, 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 EPL, the GPL or the LGPL.
 * **** END LICENSE BLOCK *****
 */
package org.jruby.embed;

import java.io.Closeable;
import java.io.File;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jnr.posix.util.Platform;
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.LocalContextProvider;
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.exceptions.RaiseException;
import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.Block;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.profile.builtin.ProfileOutput;
import org.jruby.util.ClassCache;
import org.jruby.util.JRubyClassLoader;
import org.jruby.util.KCode;
import org.jruby.util.cli.OutputStrings;
import org.jruby.util.cli.Options;

/**
 * ScriptingContainer 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 various configuration parameters.
 * Some of them are per-container properties, while others are 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. Be aware that the per-container properties should be set prior to
 * get Ruby runtime being instantiated; otherwise, default values are applied to.
 *
 * ScriptingContainer delays Ruby runtime initialization as much as possible to
 * improve startup time. When values are put into the ScriptingContainer, or runScriptlet
 * method gets run the runtime is created internally. However, the default, singleton
 * local context scope behave slightly different. If a Ruby runtime has been already
 * instantiated by another ScriptingContainer, application, etc, the same runtime
 * will be re-used.
 *
 * 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:
 * {@code
 *     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 {@code "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 a 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, {@code "x"} in Java is also {@code "x"} in Ruby. * *
 * Example 2:
 * {@code
 *     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 LocalVariableBehavior.PERSISTENT * when the container is instantiated. * *
 * Example 3:
 * {@code
 *     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:
 * {@code
 *     ScriptingContainer container = new ScriptingContainer();
 *     String script =
 *       "def message\n" +
 *         "\"message: #{@message}\"\n" +
 *       "end\n" +
 *       "message";
 *     container.put("@message", "What's up?");
 *     JavaEmbedUtils.EvalUnit unit = container.parse(script);
 *     IRubyObject msg = unit.run(); // a RubyString instance
 *     System.out.println(JavaEmbedUtils.rubyToJava(msg));
 *     container.put("@message", "Fabulous!");
 *     msg = unit.run();
 *     System.out.println(JavaEmbedUtils.rubyToJava(msg));
 *     container.put("@message", "That's the way you are.");
 *     msg = unit.run();
 *     System.out.println(JavaEmbedUtils.rubyToJava(msg));
 * }
 * 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 final Map basicProperties; private final LocalContextProvider provider; private final EmbedRubyRuntimeAdapter runtimeAdapter = new EmbedRubyRuntimeAdapterImpl(this); private final EmbedRubyObjectAdapter objectAdapter = new EmbedRubyObjectAdapterImpl(this); private final 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 scope 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 variables/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 { initRubyInstanceConfig(); } catch (RaiseException ex) { // TODO this seems useless - except that we get a Java stack trace throw new RuntimeException(ex); } basicProperties = getBasicProperties(); } static 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 : return SingletonLocalContextProvider.getProvider(behavior, lazy); } } private void initRubyInstanceConfig() throws RaiseException { List paths = SystemPropertyCatcher.findLoadPaths(); provider.getRubyInstanceConfig().setLoadPaths(paths); String home = SystemPropertyCatcher.findJRubyHome(this); if (home != null) { provider.getRubyInstanceConfig().setJRubyHome(home); } provider.getRubyInstanceConfig().setScriptFileName("