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

Commits on May 30, 2015

  1. reduce the classloader lookups for require

    URLResource always returns a FileResource, it can be either a file or
    a directory or it is marked as not existing. some classloaders return
    a stream to directory resource and some classloaders do not. to distinguish
    a file from directory you need to check for the .jrubydir file.
    
    but most creation of  the URLResource are via require 'some/thing' where
    it is clear before hand that this is a file or does not exist.
    mkristian committed May 30, 2015
    Copy the full SHA
    e4a0c32 View commit details
  2. find directory entries inside the jar if there is no .jrubydir file

    when using the uri:classloader: protocol and the lookout can be a directory
    then to determine whether it is a directory or not the following sequence takes place
    
    * look for my_directory/.jrubydir and if there use it
    * if the url from the classloader is jar url, then see if the jar file
      has a directory entry and use it (zip files may or may not have such
      directory entries depending on how it got packed)
    * if url from classloader is file url, then use the file on filesystem and
      check if it is a directory
    mkristian committed May 30, 2015
    Copy the full SHA
    29925fc View commit details
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyInstanceConfig.java
Original file line number Diff line number Diff line change
@@ -394,7 +394,7 @@ public InputStream getScriptSource() {
} else if (script.startsWith("classpath:")) {
stream = getScriptSourceFromJar(script);
} else if (script.startsWith("uri:classloader:")) {
FileResource urlResource = URLResource.create(loader, script);
FileResource urlResource = URLResource.create(loader, script, true);
stream = urlResource.inputStream();
} else {
File file = JRubyFile.create(getCurrentDirectory(), getScriptFileName());
Original file line number Diff line number Diff line change
@@ -174,7 +174,7 @@ private FoundLibrary findFileResourceWithLoadPath(String searchName, String suff
String pathWithSuffix = fullPath + suffix;

DebugLog.Resource.logTry(pathWithSuffix);
FileResource resource = JRubyFile.createResource(runtime, pathWithSuffix);
FileResource resource = JRubyFile.createResourceAsFile(runtime, pathWithSuffix);
if (resource.exists()) {
DebugLog.Resource.logFound(pathWithSuffix);
String scriptName = resolveScriptName(resource, pathWithSuffix);
8 changes: 1 addition & 7 deletions core/src/main/java/org/jruby/runtime/load/LoadService.java
Original file line number Diff line number Diff line change
@@ -971,13 +971,7 @@ private Library findLibraryBySearchState(SearchState state) {
// 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
12 changes: 10 additions & 2 deletions core/src/main/java/org/jruby/util/JRubyFile.java
Original file line number Diff line number Diff line change
@@ -63,11 +63,19 @@ public static FileResource createResource(ThreadContext context, String pathname
return createResource(context.runtime, pathname);
}

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

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

public static FileResource createResource(Ruby runtime, String cwd, String pathname) {
return createResource(runtime, cwd, pathname, false);
}

private static FileResource createResource(Ruby runtime, String cwd, String pathname, boolean isFile) {
FileResource emptyResource = EmptyFileResource.create(pathname);
if (emptyResource != null) return emptyResource;

@@ -87,7 +95,7 @@ public static FileResource createResource(Ruby runtime, String cwd, String pathn
}

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

if (pathname.startsWith("file:")) {
pathname = pathname.substring(5);
87 changes: 69 additions & 18 deletions core/src/main/java/org/jruby/util/URLResource.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
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.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
@@ -149,38 +153,91 @@ public Channel openChannel( ModeFlags flags, int perm ) throws ResourceException
return Channels.newChannel(inputStream());
}

public static FileResource create(ClassLoader cl, String pathname) {
public static FileResource create(ClassLoader cl, String pathname, boolean isFile) {
try
{
pathname = new URI(pathname.replaceFirst("^/*", "/")).normalize().getPath().replaceAll("^/([.][.]/)*", "");
} catch (URISyntaxException e) {
pathname = pathname.replaceAll("^[.]?/*", "");
}
URL url = cl.getResource(pathname);
String[] files = listClassLoaderFiles(cl, pathname);
final URL url = cl.getResource(pathname);
String[] files = null;
if (!isFile) {
files = listClassLoaderFiles(cl, pathname);
if (files == null && url != null ) {
// no .jrubydir found
boolean isDirectory = false;
// we do not want double entries
Set<String> list = new LinkedHashSet<String>();
list.add(".");
list.add("..");
try {
Enumeration<URL> urls = cl.getResources(pathname);
while(urls.hasMoreElements()){
isDirectory = addDirectoryEntries(list, urls.nextElement(), isDirectory);
}
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);
}
}
}
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 createClassloaderURI(Ruby runtime, String pathname) {
return create(runtime.getJRubyClassLoader(), pathname);
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 createClassloaderURI(Ruby runtime, String pathname, boolean isFile) {
return create(runtime.getJRubyClassLoader(), pathname, isFile);
}

public static FileResource create(Ruby runtime, String pathname)
public static FileResource create(Ruby runtime, String pathname, boolean isFile)
{
if (!pathname.startsWith(URI)) {
return null;
}
pathname = pathname.substring(URI.length());
if (pathname.startsWith(CLASSLOADER)) {
return createClassloaderURI(runtime, pathname.substring(CLASSLOADER.length()));
return createClassloaderURI(runtime, pathname.substring(CLASSLOADER.length()), isFile);
}
return createRegularURI(pathname);
return createRegularURI(pathname, isFile);
}

private static FileResource createRegularURI(String pathname) {
private static FileResource createRegularURI(String pathname, boolean isFile) {
URL url;
try
{
@@ -200,7 +257,7 @@ private static FileResource createRegularURI(String pathname) {
// file does not exists
return new URLResource(URI + pathname, (URL)null, null);
}
String[] files = listFiles(pathname);
String[] files = isFile ? null : listFiles(pathname);
if (files != null) {
return new URLResource(URI + pathname, (URL)null, files);
}
@@ -252,13 +309,10 @@ private static String[] listFilesFromInputStream(InputStream is) {
}

private static String[] listClassLoaderFiles(ClassLoader classloader, String pathname) {
if (pathname.endsWith(".rb") || pathname.endsWith(".class") || pathname.endsWith(".jar")) {
return null;
}
try
{
pathname = 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;
}
@@ -280,9 +334,6 @@ private static String[] listClassLoaderFiles(ClassLoader classloader, String pat
}

private static String[] listFiles(String pathname) {
if (pathname.endsWith(".rb") || pathname.endsWith(".class") || pathname.endsWith(".jar")) {
return null;
}
try
{
InputStream is = new URL(pathname + "/.jrubydir").openStream();
31 changes: 16 additions & 15 deletions core/src/test/java/org/jruby/util/URLResourceTest.java
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ public class URLResourceTest extends TestCase {

public void testDirectory(){
String uri = Thread.currentThread().getContextClassLoader().getResource( "somedir" ).toExternalForm();
FileResource resource = URLResource.create(Ruby.getGlobalRuntime(), "uri:" + uri);
FileResource resource = URLResource.create(Ruby.getGlobalRuntime(), "uri:" + uri, false);

assertNotNull(resource );
assertFalse(resource.isFile());
@@ -22,10 +22,9 @@ public void testDirectory(){

public void testNoneDirectory(){
String uri = Thread.currentThread().getContextClassLoader().getResource( "somedir/dir_without_listing" ).toExternalForm();
FileResource resource = URLResource.create(Ruby.getGlobalRuntime(), "uri:" + uri);
FileResource resource = URLResource.create(Ruby.getGlobalRuntime(), "uri:" + uri, false);

assertNotNull(resource );
// you can open streams on file-system directories
assertTrue(resource.isFile());
assertTrue(resource.exists());
assertFalse(resource.isDirectory());
@@ -34,10 +33,9 @@ public void testNoneDirectory(){

public void testFile(){
String uri = Thread.currentThread().getContextClassLoader().getResource( "somedir/.jrubydir" ).toExternalForm();
FileResource resource = URLResource.create(Ruby.getGlobalRuntime(), "uri:" + uri);
FileResource resource = URLResource.create(Ruby.getGlobalRuntime(), "uri:" + uri, false);

assertNotNull(resource );
// you can open streams on file-system directories
assertTrue(resource.isFile());
assertTrue(resource.exists());
assertFalse(resource.isDirectory());
@@ -46,7 +44,7 @@ public void testFile(){

public void testNonExistingFile(){
String uri = Thread.currentThread().getContextClassLoader().getResource( "somedir" ).toExternalForm();
FileResource resource = URLResource.create(Ruby.getGlobalRuntime(), "uri:" + uri + "/not_there");
FileResource resource = URLResource.create(Ruby.getGlobalRuntime(), "uri:" + uri + "/not_there", false);

assertNotNull(resource );
assertFalse(resource.isFile());
@@ -57,7 +55,8 @@ public void testNonExistingFile(){

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

assertNotNull( resource );
assertFalse( resource.isFile() );
@@ -70,22 +69,23 @@ public void testDirectoryClassloader()

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

assertNotNull( resource );
// you can open streams on file-system directories
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()
{
FileResource resource = URLResource.create(Ruby.getGlobalRuntime(), "uri:classloader:/somedir/.jrubydir" );
FileResource resource = URLResource.create(Ruby.getGlobalRuntime(),
"uri:classloader:/somedir/.jrubydir", false );

assertNotNull( resource );
// you can open streams on file-system directories
assertTrue( resource.isFile() );
assertTrue( resource.exists() );
assertFalse( resource.isDirectory() );
@@ -94,7 +94,8 @@ public void testFileClassloader()

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

assertNotNull( resource );
assertFalse( resource.isFile() );
9 changes: 9 additions & 0 deletions test/jruby/test_dir.rb
Original file line number Diff line number Diff line change
@@ -71,6 +71,15 @@ 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
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 require_relative1.rb: #{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)