Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: a1a99aaf88c0
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 814124b6e2e2
Choose a head ref
  • 2 commits
  • 9 files changed
  • 1 contributor

Commits on Jul 23, 2015

  1. have no side effects when creating instance of IsolatedScriptingConta…

    …iner
    
    backport and improve the implementation from 9k. also sets the current directory
    to uri:classloader:/ if this directory exists. same with jruby.home which will
    only be set into the classloader when it exists.
    
    Sponsored by Lookout Inc.
    mkristian committed Jul 23, 2015
    Copy the full SHA
    13a5e41 View commit details
  2. extracted the classloader to uri-like path into helper class

    and use it to provide the add classloader to LOAD_PATH or GEM_PATH
    feature for JavaEmbedUtils
    mkristian committed Jul 23, 2015
    Copy the full SHA
    814124b View commit details
57 changes: 29 additions & 28 deletions core/src/main/java/org/jruby/embed/IsolatedScriptingContainer.java
Original file line number Diff line number Diff line change
@@ -3,8 +3,12 @@
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.jruby.util.UriLikePathHelper;

/**
* the IsolatedScriptingContainer detects the whether it is used with
* a Thread.currentThread.contextClassLoader (J2EE) or with the classloader
@@ -34,6 +38,7 @@
*/
public class IsolatedScriptingContainer extends ScriptingContainer {

private static final String URI_CLASSLOADER = "uri:classloader:/";
private static final String JRUBYDIR = "/.jrubydir";
private static final String JRUBY_HOME = "/META-INF/jruby.home";

@@ -69,16 +74,21 @@ public IsolatedScriptingContainer( LocalContextScope scope,
if (cl == null) cl = Thread.currentThread().getContextClassLoader();
setClassLoader( cl );

setLoadPaths( Arrays.asList( "uri:classloader:" ) );
List<String> loadPaths = new LinkedList<String>();
loadPaths.add(URI_CLASSLOADER);
setLoadPaths(loadPaths);

// set the right jruby home
setHomeDirectory( "uri:classloader:" + JRUBY_HOME );
UriLikePathHelper uriPath = new UriLikePathHelper(cl);
URL url = uriPath.getResource(JRUBY_HOME + JRUBYDIR);
if (url != null){
setHomeDirectory( URI_CLASSLOADER + JRUBY_HOME );
}

// setup the isolated GEM_PATH, i.e. without $HOME/.gem/**
runScriptlet("require 'rubygems/defaults/jruby';"
+ "Gem::Specification.reset;"
+ "Gem::Specification.add_dir 'uri:classloader:" + JRUBY_HOME + "/lib/ruby/gems/shared';"
+ "Gem::Specification.add_dir 'uri:classloader:';");
url = uriPath.getResource(JRUBYDIR);
if (url != null){
setCurrentDirectory( URI_CLASSLOADER );
}

// setup the isolated GEM_PATH, i.e. without $HOME/.gem/**
setEnvironment(null);
@@ -87,47 +97,38 @@ public IsolatedScriptingContainer( LocalContextScope scope,
@Override
public void setEnvironment(Map environment) {
if (environment == null || !environment.containsKey("GEM_PATH")
|| !environment.containsKey("GEM_HOME")|| !environment.containsKey("JARS_HOME")) {
|| !environment.containsKey("GEM_HOME") || !environment.containsKey("JARS_HOME")) {
Map<String,String> env = environment == null ? new HashMap<String,String>() : new HashMap<String,String>(environment);
if (!env.containsKey("GEM_PATH")) env.put("GEM_PATH", "uri:classloader://");
if (!env.containsKey("GEM_HOME")) env.put("GEM_HOME", "uri:classloader://");
if (!env.containsKey("JARS_HOME")) env.put("JARS_HOME", "uri:classloader://jars");
if (!env.containsKey("GEM_PATH")) env.put("GEM_PATH", URI_CLASSLOADER);
if (!env.containsKey("GEM_HOME")) env.put("GEM_HOME", URI_CLASSLOADER);
if (!env.containsKey("JARS_HOME")) env.put("JARS_HOME", URI_CLASSLOADER + "jars");
super.setEnvironment(env);
}
else {
super.setEnvironment(environment);
}
}

public void addLoadPath( ClassLoader cl ) {
addLoadPath( cl, JRUBYDIR );
public void addLoadPath(ClassLoader cl) {
addLoadPath(new UriLikePathHelper(cl).getUriLikePath());
}

public void addLoadPath( ClassLoader cl, String ref ) {
addLoadPath(createUri(cl, ref));
addLoadPath(new UriLikePathHelper(cl).getUriLikePath(ref));
}

protected void addLoadPath(String uri) {
runScriptlet( "$LOAD_PATH << '" + uri + "' unless $LOAD_PATH.member?( '" + uri + "' )" );
if (!getLoadPaths().contains(uri)) {
getLoadPaths().add(uri);
}
}

public void addGemPath( ClassLoader cl ) {
addGemPath( cl, "/specifications" + JRUBYDIR );
addGemPath(new UriLikePathHelper(cl).getUriLikePath());
}

public void addGemPath( ClassLoader cl, String ref ) {
addGemPath(createUri(cl, ref));
}

private String createUri(ClassLoader cl, String ref) {
URL url = cl.getResource( ref );
if ( url == null && ref.startsWith( "/" ) ) {
url = cl.getResource( ref.substring( 1 ) );
}
if ( url == null ) {
throw new RuntimeException( "reference " + ref + " not found on classloader " + cl );
}
return "uri:" + url.toString().replaceFirst( ref + "$", "" );
addGemPath(new UriLikePathHelper(cl).getUriLikePath(ref));
}

protected void addGemPath(String uri) {
18 changes: 16 additions & 2 deletions core/src/main/java/org/jruby/javasupport/JavaEmbedUtils.java
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@@ -40,10 +41,11 @@
import org.jruby.RubyRuntimeAdapter;
import org.jruby.RubyString;
import org.jruby.ast.Node;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ClassCache;
import org.jruby.util.UriLikePathHelper;

/**
* Utility functions to help embedders out. These function consolidate logic that is
@@ -93,7 +95,6 @@ public static Ruby initialize(List<String> loadPaths, ClassCache classCache) {
public static Ruby initialize(List<String> loadPaths, RubyInstanceConfig config) {
Ruby runtime = Ruby.newInstance(config);
runtime.getLoadService().addPaths(loadPaths);
runtime.getLoadService().require("java");

return runtime;
}
@@ -229,13 +230,26 @@ public IRubyObject run() {
}
}

public static void addLoadPath(Ruby runtime, ClassLoader cl) {
runtime.getLoadService().addPaths(new UriLikePathHelper(cl).getUriLikePath());
}

public static void addGemPath(Ruby runtime, ClassLoader cl) {
String uri = new UriLikePathHelper(cl).getUriLikePath();
runtime.evalScriptlet("Gem::Specification.add_dir '" + uri + "' unless Gem::Specification.dirs.member?( '" + uri + "' )" );
}

/**
* Dispose of the runtime you initialized.
*
* @param runtime to be disposed of
*/
public static void terminate(Ruby runtime) {
runtime.tearDown();
try {
runtime.getJRubyClassLoader().close();
} catch (IOException weTriedToCloseIt) {
}
}

/**
62 changes: 62 additions & 0 deletions core/src/main/java/org/jruby/util/UriLikePathHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
**** 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.
*
* 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.util;

import java.net.URL;

public class UriLikePathHelper {

private final ClassLoader classLoader;

public UriLikePathHelper(ClassLoader classLoader) {
this.classLoader = classLoader;
}

public URL getResource(String ref) {
URL url = classLoader.getResource( ref );
if ( url == null && ref.startsWith( "/" ) ) {
url = classLoader.getResource( ref.substring( 1 ) );
}
return url;
}

public String getUriLikePath() {
return createUri(".jrubydir");
}

public String getUriLikePath(String ref) {
return createUri(ref);
}

String createUri(String ref) {
URL url = getResource( ref );
if ( url == null ) {
throw new RuntimeException( "reference " + ref + " not found on classloader " + classLoader );
}
return "uri:" + url.toString().replaceFirst( ref + "$", "" );
}
}
66 changes: 66 additions & 0 deletions core/src/test/java/org/jruby/javasupport/JavaEmbedUtilsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.jruby.javasupport;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import org.jruby.Ruby;
import org.jruby.RubyInstanceConfig;

public class JavaEmbedUtilsTest {

static ClassLoader cl;

@BeforeClass
public static void setupClassLoader() {
cl = Thread.currentThread().getContextClassLoader();
// make sure we have classloader which does not find jruby
ClassLoader c = new URLClassLoader( new URL[] {}, null );
try {
c.loadClass( "org.jruby.embed.ScriptingContainer" );
fail( "this classloader shall not find jruby" );
}
catch( ClassNotFoundException expected){}
// set it as context classloader
Thread.currentThread().setContextClassLoader( c );
}

@AfterClass
public static void restClassLoader() {
Thread.currentThread().setContextClassLoader( cl );
}

private static List<String> EMPTY = Collections.emptyList();

@Test
public void testAddClassloaderToLoadPathOnTCCL() throws Exception {
Thread.currentThread().setContextClassLoader( cl );
Ruby runtime = JavaEmbedUtils.initialize(EMPTY);
URL url = new File("src/test/resources/java_embed_utils").toURI().toURL();
JavaEmbedUtils.addLoadPath(runtime, new URLClassLoader(new URL[] { url }));
String result = runtime.evalScriptlet("require 'test_me'; $result").toString();
assertEquals(result, "uri:" + url);
}

@Test
public void testAddClassloaderToLoadPathOnNoneTCCL() throws Exception {
RubyInstanceConfig config = new RubyInstanceConfig();
config.setLoader(RubyInstanceConfig.class.getClassLoader());
Ruby runtime = JavaEmbedUtils.initialize(EMPTY, config);
URL url = new File("src/test/resources/java_embed_utils").toURI().toURL();
JavaEmbedUtils.addLoadPath(runtime, new URLClassLoader(new URL[] { url }));
String result = runtime.evalScriptlet("require 'test_me';$result").toString();
assertEquals(result, "uri:" + url);
}
}
Empty file.
1 change: 1 addition & 0 deletions core/src/test/resources/java_embed_utils/test_me.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$result = $LOAD_PATH.detect { |d| d =~ /^uri:/ }
8 changes: 6 additions & 2 deletions lib/ruby/shared/rubygems/defaults/jruby.rb
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@ def dirs
@@dirs ||= Gem.path.collect {|dir|
if File.file?(dir) && dir =~ /\.jar$/
"file:#{dir}!/specifications"
elsif File.directory?(dir) || dir =~ /^file:/
elsif File.directory?(File.join(dir, "specifications")) || dir =~ /^file:/
File.join(dir, "specifications")
end
}.compact + spec_directories_from_classpath
@@ -108,7 +108,11 @@ def dirs= dirs
end

def spec_directories_from_classpath
stuff = JRuby::Util.classloader_resources("specifications")
stuff = [ 'uri:classloader://specifications' ] + JRuby::Util.classloader_resources("specifications")
# some classloader return directory info. use only the "protocols"
# which jruby understands
stuff.select! { |s| File.directory?( s ) }
stuff
end
end
end
Original file line number Diff line number Diff line change
@@ -77,13 +77,13 @@ public void testJRubyCreate() throws Exception {
System.err.println();
System.err.println();

// System.setProperty( "jruby.debug.loadService", "true" );
//System.setProperty( "jruby.native.enabled", "true" );
//System.setProperty( "jruby.debug.loadService", "true" );
//System.setProperty( "jruby.native.enabled", "true" );

OSGiIsolatedScriptingContainer jruby = new OSGiIsolatedScriptingContainer();
jruby.addBundleToLoadPath( "org.jruby.osgi.scripts-bundle" );
jruby.addBundleToGemPath( FrameworkUtil.getBundle( Gems.class ) );
jruby.addBundleToLoadPath( "org.jruby.osgi.scripts-bundle" );
jruby.addBundleToGemPath( FrameworkUtil.getBundle( Gems.class ) );

// run a script from LOAD_PATH
String hello = (String) jruby.runScriptlet( "require 'hello'; Hello.say" );
assertEquals( hello, "world" );
@@ -93,7 +93,7 @@ public void testJRubyCreate() throws Exception {

String gemPath = (String) jruby.runScriptlet( "Gem::Specification.dirs.inspect" );
gemPath = gemPath.replaceAll( "bundle[^:]*://[^/]*", "bundle:/" );
assertEquals( gemPath, "[\"uri:bundle://specifications\", \"uri:classloader:/specifications\", \"uri:classloader:/META-INF/jruby.home/lib/ruby/gems/shared/specifications\"]" );
assertEquals( gemPath, "[\"uri:bundle://specifications\"]" );

// ensure we can load rake from the default gems
boolean loaded = (Boolean) jruby.runScriptlet( "require 'rake'" );
@@ -110,15 +110,15 @@ public void testJRubyCreate() throws Exception {
loaded = (Boolean) jruby.runScriptlet( "require 'openssl'" );
assertEquals(true, loaded);

jruby.runScriptlet( "require 'jar-dependencies'" );
jruby.runScriptlet( "require 'jar-dependencies'" );
list = (String) jruby.runScriptlet( "Gem.loaded_specs.keys.inspect" );
assertEquals(list, "[\"rake\", \"jruby-openssl\", \"jar-dependencies\"]");

// ensure we can load can load embedded gems
loaded = (Boolean) jruby.runScriptlet( "require 'virtus'" );
assertEquals(true, loaded);

list = (String) jruby.runScriptlet( "Gem.loaded_specs.keys.inspect" );
list = (String) jruby.runScriptlet( "Gem.loaded_specs.keys.inspect" );
assertEquals(list, "[\"rake\", \"jruby-openssl\", \"jar-dependencies\", \"thread_safe\", \"descendants_tracker\", \"equalizer\", \"coercible\", \"ice_nine\", \"axiom-types\", \"virtus\"]");
}
}
Original file line number Diff line number Diff line change
@@ -92,8 +92,10 @@ public void testJRubyCreate() throws InterruptedException {
assertEquals(true, loaded);

String gemPath = (String) jruby.runScriptlet( "Gem::Specification.dirs.inspect" );

gemPath = gemPath.replaceAll( "bundle[^:]*://[^/]*", "bundle:/" );
assertEquals( gemPath, "[\"uri:classloader:/specifications\", \"uri:classloader:/META-INF/jruby.home/lib/ruby/gems/shared/specifications\"]" );
// TODO fix the URLResource to produce uri:classloader:// urls only
assertEquals( gemPath, "[\"uri:classloader:/specifications\", \"uri:classloader://specifications\"]" );

jruby.runScriptlet( "require 'jar-dependencies'" );
list = (String) jruby.runScriptlet( "Gem.loaded_specs.keys.inspect" );