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: e29bc9a9c333
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 95b5c62bd3ad
Choose a head ref
  • 2 commits
  • 6 files changed
  • 1 contributor

Commits on Jun 1, 2015

  1. Revert "Revert "leverage the classloader + osgi-bundle support to Scr…

    …iptingContainer""
    
    This reverts commit 7719bb2.
    mkristian committed Jun 1, 2015
    Copy the full SHA
    1f96bcf View commit details
  2. move adding a classloader to LOAD_PATH and GEM_PATH to ScriptingConta…

    …iner
    
    the IsolatedScriptingContainer comes with helper to add a bundle to LOAD_PATH and GEM_PATH
    and ensure that the GEM_PATH or GEM_HOME or JARS_HOME is set inside the classloader per default,
    i.e. do not inherit the possible environment settings.
    mkristian committed Jun 1, 2015
    Copy the full SHA
    95b5c62 View commit details
139 changes: 65 additions & 74 deletions core/src/main/java/org/jruby/embed/IsolatedScriptingContainer.java
Original file line number Diff line number Diff line change
@@ -6,39 +6,26 @@
import java.util.Map;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.wiring.BundleWiring;

/**
* the IsolatedScriptingContainer detects the whether it is used with
* a Thread.currentThread.contextClassLoader (J2EE) or with the classloader
* which loaded IsolatedScriptingContainer.class (OSGi case)
* the IsolatedScriptingContainer does set GEM_HOME and GEM_PATH and JARS_HOME
* in such a way that it uses only resources which can be reached with classloader.
*
* the setup of LOAD_PATH and GEM_PATH and JRUBY_HOME uses ONLY uri: or uri:classloader:
* protocol paths. i.e. everything lives within one or more classloaders - no jars added from
* jave.class.path or similar "magics"
*
* the root of the "main" classloader is add to LOAD_PATH and GEM_PATH.
*
* in the OSGi case there are helper methods to add ClassLoaders to the LOAD_PATH or GEM_PATH
* GEM_HOME is uri:classloader://META-INF/jruby.home/lib/ruby/gems/shared
* GEM_PATH is uri:classloader://
* JARS_HOME is uri:classloader://jars
*
* a typical setup for the ContextClassLoader case and OSGi case looks likes this:
* <li>LOAD_PATH == [ "uri:classloader:/META-INF/jruby.home/lib/ruby/1.9/site_ruby",
* "uri:classloader:/META-INF/jruby.home/lib/ruby/shared",
* "uri:classloader:/META-INF/jruby.home/lib/ruby/1.9",
* "uri:classloader:" ]</li>
* <li>Gem::Specification.dirs == [ "uri:classloader:/specifications", "uri:classloader:/META-INF/jruby.home/lib/ruby/gems/shared/specifications" ]
* here very resource is loaded via <code>Thread.currentTHread.getContextClassLoader().getResourceAsStream(...)</code>
* but whenever you want to set them via {@link #setEnvironment(Map)} this will be honored.
*
* <code>new URL( uri ).openStream()</code>, i.e. <code>new URL(classloader.getResource().toString()).openStream()</code> has to work for
* those classloaders. felix, knoplerfish and equinox OSGi framework do work.
*
* NOTE: <code>Gem.path</code> is base for determine the <code>Gem::Specification.dirs</code> and <code>Gem::Specification.dirs</code> is
* used to find gemspec files of the installed gems.
* it also comes with OSGi support which allows to add a bundle to LOAD_PATH or GEM_PATH.
*/
public class IsolatedScriptingContainer extends ScriptingContainer {

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

public IsolatedScriptingContainer()
{
this(LocalContextScope.SINGLETON);
@@ -74,75 +61,79 @@ public IsolatedScriptingContainer( LocalContextScope scope,

@Override
public void setEnvironment(Map environment) {
if (environment == null || !environment.containsKey("GEM_PATH")) {
Map env = environment == null ? new HashMap() : new HashMap(environment);
env.put("GEM_PATH", "");
if (environment == null || !environment.containsKey("GEM_PATH")
|| !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:/" + JRUBY_HOME +
"/lib/ruby/gems/shared");
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, String ref ) {
addLoadPath(createUri(cl, ref));
}

public void addBundleToLoadPath( Bundle cl ) {
addBundleToLoadPath( cl, JRUBYDIR );
}

public void addBundleToLoadPath( Bundle cl, String ref ) {
addLoadPath(createUriFromBundle(cl, ref));
}

private String createUriFromBundle( Bundle cl, String ref) {
URL url = cl.getResource( ref );
if ( url == null && ref.startsWith( "/" ) ) {
url = cl.getResource( ref.substring( 1 ) );
private Bundle toBundle(String symbolicName) {
BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
Bundle bundle = null;
for (Bundle b : context.getBundles()) {
if (b.getSymbolicName().equals(symbolicName)) {
bundle = b;
break;
}
}
if ( url == null ) {
throw new RuntimeException( "reference " + ref + " not found on bundle " + cl );
if (bundle == null ) {
throw new RuntimeException("unknown bundle: " + symbolicName);
}
return "uri:" + url.toString().replaceFirst( ref + "$", "" );
}

private void addLoadPath(String uri) {
runScriptlet( "$LOAD_PATH << '" + uri + "' unless $LOAD_PATH.member?( '" + uri + "' )" );
return bundle;
}

public void addBundleToGemPath( Bundle cl ) {
addBundleToGemPath( cl, "/specifications" + JRUBYDIR );
}

public void addBundleToGemPath( Bundle cl, String ref ) {
addGemPath(createUriFromBundle(cl, ref));
}

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

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

private String createUri(ClassLoader cl, String ref) {
private String createUri(Bundle 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 );
}
System.err.println("=---" + url.toString().replaceFirst( ref + "$", "" ));
return "uri:" + url.toString().replaceFirst( ref + "$", "" );
}

private void addGemPath(String uri) {
runScriptlet( "require 'rubygems/defaults/jruby';Gem::Specification.add_dir '" + uri + "' unless Gem::Specification.dirs.member?( '" + uri + "' )" );
/**
* add the classloader from the given bundle to the LOAD_PATH
* @param bundle
*/
public void addBundleToLoadPath(Bundle bundle) {
addLoadPath(createUri(bundle, "/.jrubydir"));
}

/**
* add the classloader from the given bundle to the LOAD_PATH
* using the bundle symbolic name
*
* @param symbolicName
*/
public void addBundleToLoadPath(String symbolicName) {
addBundleToLoadPath(toBundle(symbolicName));
}

/**
* add the classloader from the given bundle to the GEM_PATH
* @param bundle
*/
public void addBundleToGemPath(Bundle bundle) {
addGemPath(createUri(bundle, "/specifications/.jrubydir"));
}

/**
* add the classloader from the given bundle to the GEM_PATH
* using the bundle symbolic name
*
* @param symbolicName
*/
public void addBundleToGemPath(String symbolicName) {
addBundleToGemPath(toBundle(symbolicName));
}
}
39 changes: 39 additions & 0 deletions core/src/main/java/org/jruby/embed/ScriptingContainer.java
Original file line number Diff line number Diff line change
@@ -30,15 +30,19 @@
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.Reader;
import java.io.Writer;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jruby.CompatVersion;
import org.jruby.Profile;
import org.jruby.Ruby;
@@ -1886,4 +1890,39 @@ public void setClassloaderDelegate(boolean classloaderDelegate) {
public boolean getClassloaderDelegate() {
return getProvider().getRubyInstanceConfig().isClassloaderDelegate();
}

/**
* add the given classloader to the LOAD_PATH
* @param classloader
*/
public void addLoadPath(ClassLoader classloader) {
addLoadPath(createUri(classloader, "/.jrubydir"));
}

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

/**
* add the given classloader to the GEM_PATH
* @param classloader
*/
public void addGemPath(ClassLoader classloader) {
addGemPath(createUri(classloader, "/specifications/.jrubydir"));
}

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 + "$", "" );
}

protected void addGemPath(String uri) {
runScriptlet( "require 'rubygems/defaults/jruby';Gem::Specification.add_dir '" + uri + "' unless Gem::Specification.dirs.member?( '" + uri + "' )" );
}
}
Original file line number Diff line number Diff line change
@@ -180,5 +180,5 @@ public void addToClassPath(Bundle bundle) {
public JRubyOSGiBundleClassLoader getOSGiBundleClassLoader() {
return (JRubyOSGiBundleClassLoader)super.getClassLoader();
}

}
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" );

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

IsolatedScriptingContainer jruby = new IsolatedScriptingContainer();
jruby.addLoadPath( Scripts.class.getClassLoader() );
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:/META-INF/jruby.home/lib/ruby/gems/shared/specifications\", \"uri:classloader:/specifications\"]" );
assertEquals( gemPath, "[\"uri:bundle://specifications\", \"uri:classloader://META-INF/jruby.home/lib/ruby/gems/shared/specifications\", \"uri:classloader:/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\"]");
}
}
154 changes: 154 additions & 0 deletions maven/jruby/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jruby</groupId>
<artifactId>jruby-artifacts</artifactId>
<version>9.0.0.0-SNAPSHOT</version>
</parent>
<artifactId>jruby</artifactId>
<name>JRuby Main Maven Artifact</name>
<properties>
<tesla.dump.readonly>true</tesla.dump.readonly>
<main.basedir>${project.parent.parent.basedir}</main.basedir>
<tesla.dump.pom>pom.xml</tesla.dump.pom>
</properties>
<dependencies>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-stdlib</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<configuration>
<skipSource>true</skipSource>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<executions>
<execution>
<id>clean-extra-osgi-ITs</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
<configuration>
<filesets>
<fileset>
<directory>${basedir}/src/it</directory>
<includes>
<include>osgi*/**</include>
</includes>
</fileset>
</filesets>
<failOnError>false</failOnError>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.takari.polyglot</groupId>
<artifactId>polyglot-maven-plugin</artifactId>
<version>0.1.10</version>
<executions>
<execution>
<id>setup other osgi frameworks</id>
<phase>pre-integration-test</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<taskId>setup other osgi frameworks</taskId>
<nativePom>pom.rb</nativePom>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>io.takari.polyglot</groupId>
<artifactId>polyglot-ruby</artifactId>
<version>0.1.10</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jdk8</id>
<activation>
<jdk>1.8</jdk>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
<configuration>
<pomExcludes>
<pomExclude>extended/pom.xml</pomExclude>
<pomExclude>osgi_all_inclusive_felix-3.2/pom.xml</pomExclude>
<pomExclude>${its.j2ee}</pomExclude>
<pomExclude>${its.osgi}</pomExclude>
</pomExcludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>wlp</id>
<activation>
<property>
<name>wlp.jar</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>io.takari.polyglot</groupId>
<artifactId>polyglot-maven-plugin</artifactId>
<version>0.1.10</version>
<executions>
<execution>
<id>install_wlp</id>
<phase>pre-integration-test</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<taskId>install_wlp</taskId>
<nativePom>pom.rb</nativePom>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>io.takari.polyglot</groupId>
<artifactId>polyglot-ruby</artifactId>
<version>0.1.10</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
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:/META-INF/jruby.home/lib/ruby/gems/shared/specifications\", \"uri:classloader://specifications\"]" );
// TODO fix the URLResource to produce uri:classloader:// urls only
assertEquals( gemPath, "[\"uri:classloader:/specifications\", \"uri:classloader://META-INF/jruby.home/lib/ruby/gems/shared/specifications\", \"uri:classloader://specifications\"]" );

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