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

Commits on Dec 3, 2014

  1. redefine uri:classloader: meaning to be the parent classloader of run…

    …time.getJRubyClassLoader
    
    this allows to set JRubyHome to uri:classloader:/META-INF/jruby.home for all situations where
    there is a jruby.home packed inside a jar.
    
    fixes problem with loadService and knoplerfish OSGi
    
    fixed missed test and cleanup javadocs and add more junit-tests for uri:classloader:
    
    make IsolatedScriptingContainer work on felix-4.2.1 and probably on other osgi frameworks outside the pax test cases
    mkristian committed Dec 3, 2014
    Copy the full SHA
    b672b80 View commit details
  2. add testcase for the case where jruby.home is inside a jar but not on…

    … the context-classloader
    mkristian committed Dec 3, 2014
    Copy the full SHA
    99ff77d View commit details
60 changes: 20 additions & 40 deletions core/src/main/java/org/jruby/embed/IsolatedScriptingContainer.java
Original file line number Diff line number Diff line change
@@ -18,23 +18,16 @@
*
* in the OSGi case there are helper methods to add ClassLoaders to the LOAD_PATH or GEM_PATH
*
* a typical setup for the ContextClassLoader case looks likes this:
* 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:", "uri:classloader:/META-INF/jruby.home/lib/ruby/gems/shared" ]
* <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>
*
* a typical setup for OSGi case (one bundle with everything):
* <li>LOAD_PATH == [ "uri:bundle://16.0:1/META-INF/jruby.home/lib/ruby/1.9/site_ruby",
* "uri:bundle://16.0:1/META-INF/jruby.home/lib/ruby/shared",
* "uri:bundle://16.0:1/META-INF/jruby.home/lib/ruby/1.9",
* "uri:bundle://16.0:1" ]</li>
* <li>Gem::Specification.dirs == [ "uri:bundle://16.0:1", "uri:bundle://16.0:1/META-INF/jruby.home/lib/ruby/gems/shared" ]
* other OSGi frameworks use other uris like bundleresource:/16.fwk1661197821. here very resource is loaded via
* <code>new URL( uri )openStream()</code>, i.e. <code>new URL(classloader.getResource().toString()).openStream()</code> has to work for
* those classloaders. felix and equinox OSGi framework do work.
* <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.
@@ -43,7 +36,6 @@ public class IsolatedScriptingContainer extends ScriptingContainer {

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

public IsolatedScriptingContainer()
{
@@ -70,37 +62,25 @@ public IsolatedScriptingContainer( LocalContextScope scope,
LocalVariableBehavior behavior,
boolean lazy )
{
super( scope, behavior, lazy );
URL home = Thread.currentThread().getContextClassLoader().getResource( JRUBY_HOME_DIR.substring( 1 ) );
final String baseuri;
if ( home == null ) {
home = this.getClass().getClassLoader().getResource( JRUBY_HOME_DIR );
if ( home == null ) {
throw new RuntimeException( "BUG can not find " + JRUBY_HOME_DIR );
}
setClassLoader( this.getClass().getClassLoader() );
setHomeDirectory( "uri:" + home.toString().replaceFirst( JRUBYDIR + "$", "" ) );
baseuri = createUri(getClassLoader(), "/jruby/java.rb" );
}
else {
setHomeDirectory( "uri:classloader:" + JRUBY_HOME );
baseuri = "uri:classloader:/";
}
super(scope, behavior, lazy);

// get the right classloader
ClassLoader cl = this.getClass().getClassLoader();
if (cl == null) cl = Thread.currentThread().getContextClassLoader();
setClassLoader( cl );

setLoadPaths( Arrays.asList( "uri:classloader:" ) );

// clean up LOAD_PATH
getProvider().getRubyInstanceConfig().setLoadPaths(Arrays.asList(baseuri));
runScriptlet( "$LOAD_PATH.delete_if{|p| p =~ /jar$/ };"
// TODO NormalizedFile does too much - should leave uri: files as they are
+ "$LOAD_PATH.each{|p| p.sub!( /:\\/([^\\/])/,'://\\1' )}" );

runScriptlet( "require 'rubygems/defaults/jruby';" // make sure we have the monkey patch Gem::Specification
// set the right jruby home
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 '" + getHomeDirectory() + "/lib/ruby/gems/shared';"
// if jruby-core and jruby-stdlib comes from the same osgi bundle, assume the embedded gems
// are in the same bundle
+ (getHomeDirectory().startsWith(baseuri) ? "Gem::Specification.add_dir '" + baseuri + "';" : "" ) );
+ "Gem::Specification.add_dir 'uri:classloader:" + JRUBY_HOME + "/lib/ruby/gems/shared';"
+ "Gem::Specification.add_dir 'uri:classloader:';");
}

public void addLoadPath( ClassLoader cl ) {
addLoadPath( cl, JRUBYDIR );
}
Original file line number Diff line number Diff line change
@@ -282,7 +282,7 @@ private void loadJar(Ruby runtime, boolean wrap) {
}
else if (location.startsWith(URLResource.URI)){
url = null;
runtime.getJRubyClassLoader().addURLNoIndex(URLResource.getResourceURL(location));
runtime.getJRubyClassLoader().addURLNoIndex(URLResource.getResourceURL(runtime, location));
}
else {
File f = new File(location);
15 changes: 10 additions & 5 deletions core/src/main/java/org/jruby/runtime/load/LoadService.java
Original file line number Diff line number Diff line change
@@ -1003,12 +1003,17 @@ private Library findLibraryBySearchState(SearchState state) {
return state.library;
}

// TODO(ratnikov): Remove the special classpath case by introducing a classpath file resource
Library library = findLibraryWithClassloaders(state, state.searchFile, state.suffixType);
if (library != null) {
state.library = library;
// this test is needed to avoid an in org.jruby.runtime.load.LoadService.findFileInClasspath
// running inside felix-4.2.1 osgi framework
if (loadPath.size() == 0 || !loadPath.first().asJavaString().equals("uri:classloader:")) {
// TODO(ratnikov): Remove the special classpath case by introducing a classpath file resource
Library library = findLibraryWithClassloaders(state, state.searchFile, state.suffixType);
if (library != null) {
state.library = library;
}
return library;
}
return library;
return null;
}

@Deprecated
8 changes: 6 additions & 2 deletions core/src/main/java/org/jruby/util/JRubyFile.java
Original file line number Diff line number Diff line change
@@ -64,10 +64,14 @@ public static FileResource createResource(ThreadContext context, String pathname
}

public static FileResource createResource(Ruby runtime, String pathname) {
return createResource(runtime.getPosix(), runtime.getCurrentDirectory(), pathname);
return createResource(runtime.getPosix(), runtime, runtime.getCurrentDirectory(), pathname);
}

public static FileResource createResource(POSIX posix, String cwd, String pathname) {
return createResource(posix, null, cwd, pathname);
}

public static FileResource createResource(POSIX posix, Ruby runtime, String cwd, String pathname) {
FileResource emptyResource = EmptyFileResource.create(pathname);
if (emptyResource != null) return emptyResource;

@@ -80,7 +84,7 @@ public static FileResource createResource(POSIX posix, String cwd, String pathna
if (pathname.startsWith("classpath:")) return ClasspathResource.create(pathname);

// replace is needed for maven/jruby-complete/src/it/app_using_classpath_uri to work
if (pathname.startsWith("uri:")) return URLResource.create(pathname.replace("classpath:/", ""));
if (pathname.startsWith("uri:")) return URLResource.create(runtime, pathname.replace("classpath:/", ""));

if (pathname.startsWith("file:")) {
pathname = pathname.substring(5);
57 changes: 31 additions & 26 deletions core/src/main/java/org/jruby/util/URLResource.java
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@

import jnr.posix.FileStat;

import org.jruby.Ruby;
import org.jruby.util.io.ChannelDescriptor;
import org.jruby.util.io.ModeFlags;

@@ -31,19 +32,21 @@ public class URLResource implements FileResource {
private final String pathname;

private final JarFileStat fileStat;
private final ClassLoader cl;

URLResource(String uri, URL url, String[] files) {
this(uri, url, null, files);
this(uri, url, null, null, files);
}

URLResource(String uri, String pathname, String[] files) {
this(uri, null, pathname, files);
URLResource(String uri, ClassLoader cl, String pathname, String[] files) {
this(uri, null, cl, pathname, files);
}

private URLResource(String uri, URL url, String pathname, String[] files) {
private URLResource(String uri, URL url, ClassLoader cl, String pathname, String[] files) {
this.uri = uri;
this.list = files;
this.url = url;
this.cl = cl;
this.pathname = pathname;
this.fileStat = new JarFileStat(this);
}
@@ -126,7 +129,7 @@ public FileStat lstat() {

@Override
public JRubyFile hackyGetJRubyFile() {
return JRubyNonExistentFile.NOT_EXIST;
return JRubyNonExistentFile.NOT_EXIST;
}

@Override
@@ -135,7 +138,7 @@ public InputStream openInputStream()
try
{
if (pathname != null) {
return Thread.currentThread().getContextClassLoader().getResourceAsStream(pathname);
return cl.getResourceAsStream(pathname);
}
return url.openStream();
}
@@ -153,33 +156,32 @@ public ChannelDescriptor openDescriptor(ModeFlags flags, int perm) throws Resour
return new ChannelDescriptor(openInputStream(), flags);
}

public static FileResource createClassloaderURI(String pathname) {
public static FileResource createClassloaderURI(Ruby runtime, String pathname) {
// retrieve the classloader from the runtime if available otherwise mimic how the runtime got its classloader
// and use this classloader
ClassLoader cl = runtime != null ? runtime.getJRubyClassLoader() : URLResource.class.getClassLoader();
if (cl == null ) {
cl = Thread.currentThread().getContextClassLoader();
}
if (pathname.startsWith("/")) {
pathname = pathname.substring(1);
}
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(pathname);
if (is != null) {
try
{
is.close();
}
// need Exception here due to strange NPE in some cases
catch (Exception e) {}
}
String[] files = listClassLoaderFiles(pathname);
URL url = cl.getResource(pathname);
String[] files = listClassLoaderFiles(cl, pathname);
return new URLResource(URI_CLASSLOADER + pathname,
is == null ? null : pathname,
cl,
url == null ? null : pathname,
files);
}

public static FileResource create(String pathname)
public static FileResource create(Ruby runtime, String pathname)
{
if (!pathname.startsWith(URI)) {
return null;
}
pathname = pathname.substring(URI.length());
if (pathname.startsWith(CLASSLOADER)) {
return createClassloaderURI(pathname.substring(CLASSLOADER.length()));
return createClassloaderURI(runtime, pathname.substring(CLASSLOADER.length()));
}
return createRegularURI(pathname);
}
@@ -189,7 +191,10 @@ private static FileResource createRegularURI(String pathname) {
try
{
// TODO NormalizedFile does too much - should leave uri: files as they are
// and make file:/a protocol to be file:///a so the second replace does not apply
pathname = pathname.replaceFirst( "file:/([^/])", "file:///$1" );
pathname = pathname.replaceFirst( ":/([^/])", "://$1" );

url = new URL(pathname);
// we do not want to deal with those url here like this though they are valid url/uri
if (url.getProtocol().startsWith("http")){
@@ -198,6 +203,7 @@ private static FileResource createRegularURI(String pathname) {
}
catch (MalformedURLException e)
{
e.printStackTrace();
// file does not exists
return new URLResource(URI + pathname, (URL)null, null);
}
@@ -251,13 +257,13 @@ private static String[] listFilesFromInputStream(InputStream is) {
}
}
}
private static String[] listClassLoaderFiles(String pathname) {
private static String[] listClassLoaderFiles(ClassLoader cl, String pathname) {
if (pathname.endsWith(".rb") || pathname.endsWith(".class") || pathname.endsWith(".jar")) {
return null;
}
try
{
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(pathname + "/.jrubydir");
Enumeration<URL> urls = cl.getResources(pathname + "/.jrubydir");
if (!urls.hasMoreElements()) {
return null;
}
@@ -284,8 +290,7 @@ private static String[] listFiles(String pathname) {
}
try
{
// TODO remove this replace
InputStream is = new URL(pathname.replace("file://", "file:/") + "/.jrubydir").openStream();
InputStream is = new URL(pathname + "/.jrubydir").openStream();
// no inputstream happens with knoplerfish OSGI and osgi tests from /maven/jruby-complete
if (is != null) {
return listFilesFromInputStream(is);
@@ -300,10 +305,10 @@ private static String[] listFiles(String pathname) {
}
}

public static URL getResourceURL(String location)
public static URL getResourceURL(Ruby runtime, String location)
{
if (location.startsWith(URI + CLASSLOADER)){
return Thread.currentThread().getContextClassLoader().getResource(location.substring(URI_CLASSLOADER.length()));
return runtime.getJRubyClassLoader().getResource(location.substring(URI_CLASSLOADER.length()));
}
try
{
60 changes: 52 additions & 8 deletions core/src/test/java/org/jruby/util/URLResourceTest.java
Original file line number Diff line number Diff line change
@@ -8,8 +8,7 @@ public class URLResourceTest extends TestCase {

public void testDirectory(){
String uri = Thread.currentThread().getContextClassLoader().getResource( "somedir" ).toExternalForm();
// hmm not sure why the url from the classloader does not work :(
FileResource resource = URLResource.create( "uri:" + uri.replace( "file://", "file:/" ));
FileResource resource = URLResource.create( null, "uri:" + uri);

assertNotNull(resource );
assertFalse(resource.isFile());
@@ -21,8 +20,7 @@ public void testDirectory(){

public void testNoneDirectory(){
String uri = Thread.currentThread().getContextClassLoader().getResource( "somedir/dir_without_listing" ).toExternalForm();
// TODO once the URLResource does keep the protocol part of the uri as is we can remove this replace
FileResource resource = URLResource.create( "uri:" + uri.replace( "file:/", "file:///" ));
FileResource resource = URLResource.create( null, "uri:" + uri);

assertNotNull(resource );
// you can open streams on file-system directories
@@ -34,8 +32,7 @@ public void testNoneDirectory(){

public void testFile(){
String uri = Thread.currentThread().getContextClassLoader().getResource( "somedir/.jrubydir" ).toExternalForm();
// TODO once the URLResource does keep the protocol part of the uri as is we can remove this replace
FileResource resource = URLResource.create( "uri:" + uri.replace( "file:/", "file:///" ));
FileResource resource = URLResource.create( null, "uri:" + uri);

assertNotNull(resource );
// you can open streams on file-system directories
@@ -47,13 +44,60 @@ public void testFile(){

public void testNonExistingFile(){
String uri = Thread.currentThread().getContextClassLoader().getResource( "somedir" ).toExternalForm();
// TODO once the URLResource does keep the protocol part of the uri as is we can remove this replace
FileResource resource = URLResource.create( "uri:" + uri.replace( "file:/", "file:///" ) + "/not_there");
FileResource resource = URLResource.create( null, "uri:" + uri + "/not_there");

assertNotNull(resource );
assertFalse(resource.isFile());
assertFalse(resource.exists());
assertFalse(resource.isDirectory());
assertNull(resource.list());
}

public void testDirectoryClassloader()
{
FileResource resource = URLResource.create( null, "uri:classloader:/somedir");

assertNotNull( resource );
assertFalse( resource.isFile() );
assertTrue( resource.isDirectory() );
assertTrue( resource.exists() );
assertEquals( Arrays.asList( resource.list() ),
Arrays.asList( new String[] { ".", "dir_without_listing",
"dir_with_listing" } ) );
}

public void testNoneDirectoryClassloader()
{
FileResource resource = URLResource.create( null, "uri:classloader:/somedir/dir_without_listing");

assertNotNull( resource );
// you can open streams on file-system directories
assertTrue( resource.isFile() );
assertTrue( resource.exists() );
assertFalse( resource.isDirectory() );
assertNull( resource.list() );
}

public void testFileClassloader()
{
FileResource resource = URLResource.create( null, "uri:classloader:/somedir/.jrubydir" );

assertNotNull( resource );
// you can open streams on file-system directories
assertTrue( resource.isFile() );
assertTrue( resource.exists() );
assertFalse( resource.isDirectory() );
assertNull( resource.list() );
}

public void testNonExistingFileClassloader()
{
FileResource resource = URLResource.create( null, "uri:classloader:/somedir/not_there" );

assertNotNull( resource );
assertFalse( resource.isFile() );
assertFalse( resource.exists() );
assertFalse( resource.isDirectory() );
assertNull( resource.list() );
}
}
Loading