Skip to content

Commit

Permalink
Showing 14 changed files with 135 additions and 389 deletions.
Original file line number Diff line number Diff line change
@@ -63,7 +63,18 @@ public OSGiLoadService(Ruby runtime) {
super(runtime);
// super.searchers.add(new OSGiBundlesSearcher());
}


protected Library findLibraryBySearchState(SearchState state) {
Library library = super.findLibraryBySearchState(state);
if (library == null){
library = findLibraryWithClassloaders(state, state.searchFile, state.suffixType);
if (library != null) {
state.library = library;
}
}
return library;
}

/**
* Support for 'bundle:/' to look for libraries in osgi bundles
* or classes or ruby files.
Original file line number Diff line number Diff line change
@@ -292,9 +292,10 @@ public void load(Ruby runtime, boolean wrap) {
try {
URL url;
if (location.startsWith(URLResource.URI)) {
url = null;
runtime.getJRubyClassLoader().addURLNoIndex(URLResource.getResourceURL(runtime, location));
url = URLResource.getResourceURL(runtime, location);
} else {
// convert file urls with !/ into jar urls so the classloader
// can handle them via protocol handler
File f = new File(location);
if (f.exists() || location.contains( "!")){
url = f.toURI().toURL();
@@ -305,9 +306,7 @@ public void load(Ruby runtime, boolean wrap) {
url = new URL(location);
}
}
if (url != null) {
runtime.getJRubyClassLoader().addURL(url);
}
runtime.getJRubyClassLoader().addURL(url);
} catch (MalformedURLException badUrl) {
runtime.newIOErrorFromException(badUrl);
}
10 changes: 2 additions & 8 deletions core/src/main/java/org/jruby/runtime/load/LoadService.java
Original file line number Diff line number Diff line change
@@ -966,18 +966,12 @@ protected void debugLogFound( LoadServiceResource resource ) {
}
}

private Library findLibraryBySearchState(SearchState state) {
protected Library findLibraryBySearchState(SearchState state) {
if (librarySearcher.findBySearchState(state) != null) {
// findBySearchState should fill the state already
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;
}
return library;
return null;
}

@Deprecated
182 changes: 0 additions & 182 deletions core/src/main/java/org/jruby/util/CompoundJarURLStreamHandler.java

This file was deleted.

187 changes: 10 additions & 177 deletions core/src/main/java/org/jruby/util/JRubyClassLoader.java
Original file line number Diff line number Diff line change
@@ -30,23 +30,12 @@
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
@@ -55,15 +44,15 @@ public class JRubyClassLoader extends ClassDefiningJRubyClassLoader {

private static final Logger LOG = LoggerFactory.getLogger("JRubyClassLoader");

private final Map<URL,Set<String>> jarIndexes = new LinkedHashMap<URL,Set<String>>();

private Runnable unloader;

public JRubyClassLoader(ClassLoader parent) {
super(parent);
}

public void addURLNoIndex(URL url) {
// Change visibility so others can see it
@Override
public void addURL(URL url) {
// if we have such embedded jar within a jar, we copy it to temp file and use the
// the temp file with the super URLClassLoader
if ( url.toString().contains( "!/" )) {
@@ -113,13 +102,6 @@ public void addURLNoIndex(URL url) {
super.addURL( url );
}

// Change visibility so others can see it
@Override
public void addURL(URL url) {
super.addURL(url);
indexJarContents(url);
}

/**
* Called when the parent runtime is torn down.
*/
@@ -129,9 +111,13 @@ public void tearDown(boolean debug) {
// See http://bugs.jruby.org/4226
getJDBCDriverUnloader().run();
} catch (Exception e) {
if (debug) {
LOG.debug(e);
}
if (debug) LOG.debug(e);
}
// close the resources of the classloader as well
try {
close();
} catch (IOException e) {
if (debug) LOG.debug(e);
}
}

@@ -154,157 +140,4 @@ public synchronized Runnable getJDBCDriverUnloader() {
}
return unloader;
}

@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
try {
return super.findClass(className);
} catch (ClassNotFoundException ex) {
String resourceName = className.replace('.', '/').concat(".class");

URL classUrl = null;
synchronized (jarIndexes) {
for (URL jarUrl : jarIndexes.keySet()) {
if (jarIndexes.get(jarUrl).contains(resourceName)) {
try {
classUrl = CompoundJarURLStreamHandler.createUrl(jarUrl, resourceName);
break;
} catch (IOException e) {
// keep going to next URL
}
}
}
}

if (classUrl != null) {
try {
InputStream input = classUrl.openStream();
try {
byte[] buffer = new byte[4096];
ByteArrayOutputStream output = new ByteArrayOutputStream();

for (int count = input.read(buffer); count > 0; count = input.read(buffer)) {
output.write(buffer, 0, count);
}

byte[] data = output.toByteArray();
return defineClass(className, data, 0, data.length);
} finally {
close(input);
}
} catch (IOException e) {
// just fall-through to the re-throw below
}
}

throw ex;
}
}

@Override
public URL findResource(String resourceName) {
URL result = super.findResource(resourceName);

if (result == null) {
synchronized (jarIndexes) {
for (URL jarUrl : jarIndexes.keySet()) {
if (jarIndexes.get(jarUrl).contains(resourceName)) {
try {
return CompoundJarURLStreamHandler.createUrl(jarUrl, resourceName);
} catch (IOException e) {
// keep going
}
}
}
}
}

return result;
}

@Override
public Enumeration<URL> findResources(String resourceName) throws IOException {
final List<URL> embeddedUrls = new ArrayList<URL>();

synchronized (jarIndexes) {
for (URL jarUrl : jarIndexes.keySet()) {
if (jarIndexes.get(jarUrl).contains(resourceName)) {
try {
embeddedUrls.add(CompoundJarURLStreamHandler.createUrl(jarUrl, resourceName));
} catch (IOException e) {
// keep going
}
}
}
}

if (embeddedUrls.isEmpty()) {
return super.findResources(resourceName);
} else {
final Enumeration<URL> originalResult = super.findResources(resourceName);

return new Enumeration<URL>() {
private Iterator<URL> extendedResult;

public URL nextElement() {
if (extendedResult == null) {
return originalResult.nextElement();
} else {
return extendedResult.next();
}
}

public boolean hasMoreElements() {
if (extendedResult == null) {
boolean result = originalResult.hasMoreElements();

if (!result) {
// original result is consumed, switching to result
// from embedded jars processing.
extendedResult = embeddedUrls.iterator();
result = extendedResult.hasNext();
}
return result;
} else {
return extendedResult.hasNext();
}
}
};
}
}

private void indexJarContents(URL jarUrl) {
String proto = jarUrl.getProtocol();
// we only need to index jar: and compoundjar: URLs
// 1st-level jar files with file: URLs are handled by the JDK
if (proto.equals("jar") || proto.equals(CompoundJarURLStreamHandler.PROTOCOL)) {
synchronized (jarIndexes) {
Set<String> entries = new HashSet<String>();
jarIndexes.put(jarUrl, entries);

try {
InputStream baseInputStream = jarUrl.openStream();
try {
JarInputStream baseJar = new JarInputStream(baseInputStream);
for (JarEntry entry = baseJar.getNextJarEntry(); entry != null; entry = baseJar.getNextJarEntry()) {
entries.add(entry.getName());
}
} finally {
close(baseInputStream);
}
} catch (IOException ex) {
// can't read the stream, keep going
}
}
}
}

private static void close(Closeable resource) {
if (resource != null) {
try {
resource.close();
} catch (IOException ignore) {
}
}
}
}
87 changes: 79 additions & 8 deletions core/src/main/java/org/jruby/util/URLResource.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package org.jruby.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.channels.Channel;
import java.nio.channels.Channels;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -157,17 +160,85 @@ public static FileResource createClassloaderURI(Ruby runtime, String pathname, b
pathname = new URI(pathname.replaceFirst("^/*", "/")).normalize().getPath().replaceAll("^/([.][.]/)*", "");
} catch (URISyntaxException e) {
pathname = pathname.replaceAll("^[.]?/*", "");
}
URL url = cl.getResource(pathname);
String[] files = isFile ? null : listClassLoaderFiles(cl, pathname);
return new URLResource(URI_CLASSLOADER + "/" + pathname,
}
final URL url = cl.getResource(pathname);
String[] files = null;
if (!isFile) {
files = listClassLoaderFiles(cl, pathname);
if (files == null) {
// no .jrubydir found
boolean isDirectory = false;
// we do not want double entries
Set<String> list = new LinkedHashSet<String>();
list.add(".");
list.add("..");
try {
// first look at the enum from the classloader
// may or may not contain directory entries
Enumeration<URL> urls = cl.getResources(pathname);
while(urls.hasMoreElements()){
isDirectory = addDirectoryEntries(list, urls.nextElement(), isDirectory);
}
if (runtime != null) {
// we have a runtime, so look at the JRubyClassLoader
// and its parent classloader
isDirectory = addDirectoriesFromClassloader(cl, list, pathname, isDirectory);
isDirectory = addDirectoriesFromClassloader(cl.getParent(), list, pathname, isDirectory);
}
else {
// just look at what we have
isDirectory = addDirectoriesFromClassloader(cl, list, pathname, isDirectory);
}
if (isDirectory) files = list.toArray(new String[list.size()]);

} catch (IOException e) {
// we tried
}
}
}
return new URLResource(URI_CLASSLOADER + '/' + pathname,
cl,
url == null ? null : pathname,
files);
}

public static FileResource create(Ruby runtime, String pathname, boolean isFile)
{
private static boolean addDirectoriesFromClassloader(ClassLoader cl, Set<String> list, String pathname, boolean isDirectory) throws IOException {
if (cl instanceof URLClassLoader ) {
for(URL u : ((URLClassLoader)cl).getURLs()){
if (u.getFile().endsWith(".jar") && u.getProtocol().equals("file")){
u = new URL("jar:" + u + "!/" + pathname);
isDirectory = addDirectoryEntries(list, u, isDirectory);
}
}
}
return isDirectory;
}

private static boolean addDirectoryEntries(Set<String> entries, URL url,
boolean isDirectory) {
switch (url.getProtocol()) {
case "jar":
// maybe the jar itself contains directory entries (which are actually optional)
FileResource jar = JarResource.create(url.toString());
if (jar != null && jar.isDirectory()) {
if (!isDirectory) isDirectory = true;
entries.addAll(Arrays.asList(jar.list()));
}
break;
case "file":
// let's look on the filesystem
File file = new File(url.getPath());
if (file.isDirectory()) {
if (!isDirectory) isDirectory = true;
entries.addAll(Arrays.asList(file.list()));
}
break;
default:
}
return isDirectory;
}

public static FileResource create(Ruby runtime, String pathname, boolean isFile) {
if (!pathname.startsWith(URI)) {
return null;
}
@@ -253,8 +324,8 @@ private static String[] listFilesFromInputStream(InputStream is) {
private static String[] listClassLoaderFiles(ClassLoader classloader, String pathname) {
try
{
pathname += pathname.equals("") ? ".jrubydir" : "/.jrubydir";
Enumeration<URL> urls = classloader.getResources(pathname);
String path = pathname + (pathname.equals("") ? ".jrubydir" : "/.jrubydir");
Enumeration<URL> urls = classloader.getResources(path);
if (!urls.hasMoreElements()) {
return null;
}
7 changes: 4 additions & 3 deletions core/src/test/java/org/jruby/util/URLResourceTest.java
Original file line number Diff line number Diff line change
@@ -73,10 +73,11 @@ public void testNoneDirectoryClassloader()
"uri:classloader:/somedir/dir_without_listing", false);

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

public void testFileClassloader()
2 changes: 1 addition & 1 deletion maven/jruby/src/it/jetty/Mavenfile
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@ execute 'check download', :phase => :verify do
unless result.match( /^#{expected}/ )
raise "missed expected string in download: #{expected}"
end
expected = 'classes/gems/flickraw-0.9.7'
expected = 'uri:classloader:/gems/flickraw-0.9.7'
unless result.match( /#{expected}/ )
raise "missed expected string in download: #{expected}"
end
2 changes: 1 addition & 1 deletion maven/jruby/src/it/tomcat/pom.rb
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@
unless result.match( /#{expected}/ )
raise "missed expected string in download: #{expected}"
end
expected = 'classes/gems/flickraw-0.9.7'
expected = 'uri:classloader:/gems/flickraw-0.9.7'
unless result.match( /#{expected}/ )
raise "missed expected string in download: #{expected}"
end
2 changes: 1 addition & 1 deletion rakelib/rspec.rake
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ namespace :spec do
end

permute_specs "profiler", compile_flags do |t|
t.ruby_opts = ["--profile"]
t.ruby_opts = ['-I.', "--profile"]
t.pattern = 'spec/profiler/**/*_spec.rb'
end

2 changes: 1 addition & 1 deletion rakelib/test.rake
Original file line number Diff line number Diff line change
@@ -125,7 +125,7 @@ namespace :test do
t.test_files = files
t.verbose = true
t.test_files = files_in_file 'test/slow.index'
t.ruby_opts << '-J-ea' << '--1.8'
t.ruby_opts << '-J-ea' << '-I.'
t.ruby_opts << '-J-cp target/test-classes'
end

2 changes: 1 addition & 1 deletion spec/profiler/runtime_spec.rb
Original file line number Diff line number Diff line change
@@ -49,7 +49,7 @@ def check_passed_spec(outcome)
end

def new_runtime(config = Java::OrgJruby::RubyInstanceConfig.new)
config.processArguments(['--profile.api'])
config.processArguments(['-I.', '--profile.api'])
Java::OrgJruby::Ruby.newInstance(config)
end

11 changes: 11 additions & 0 deletions test/jruby/test_dir.rb
Original file line number Diff line number Diff line change
@@ -71,6 +71,17 @@ def test_dir_entries
assert_equal(['.', '..', "file1", "file2"], files.sort)
end

def test_entries_via_uri_classloader
jar_file = File.expand_path('../jar_with_relative_require1.jar', __FILE__)
$CLASSPATH << jar_file
jruby_dir = File.expand_path('../../../', __FILE__)
$CLASSPATH << jruby_dir
jar_path = "uri:classloader:/test"
dir = Dir.new(jar_path)
assert dir.entries.include?('require_relative1.rb'), "#{jar_path} does not contain require_relative1.rb: #{dir.entries.inspect}"
assert dir.entries.include?('check_versions.sh'), "#{jar_path} does not contain check_versions.sh: #{dir.entries.inspect}"
end

def test_bogus_glob
# Test unescaped special char that is meant to be used with another
# (i.e. bogus glob pattern)
8 changes: 8 additions & 0 deletions test/jruby/test_load.rb
Original file line number Diff line number Diff line change
@@ -46,6 +46,14 @@ def test_require
assert $loaded_foo_bar
end

def test_require_without_current_dir_in_load_path
$LOAD_PATH.delete '.'
assert_raises(LoadError) { require('test/jruby/dummy') }
assert require('./test/jruby/dummy')
ensure
$LOAD_PATH << '.'
end

# GH-2972
def test_require_relative_via_uri_classloader_protocol
$CLASSPATH << './test/jruby'

0 comments on commit 5b411a2

Please sign in to comment.