Navigation Menu

Skip to content

Commit

Permalink
make the regular classloader load path default again
Browse files Browse the repository at this point in the history
add an option which allows to switch to a self-first strategy which might
be able to solve some classloader conflicts.

keep the old tests using the switch to use the SelfFirstClassLoader
  • Loading branch information
mkristian committed Jan 28, 2015
1 parent d12289e commit 580bd4e
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 28 deletions.
8 changes: 7 additions & 1 deletion core/src/main/java/org/jruby/Ruby.java
Expand Up @@ -141,6 +141,7 @@
import org.jruby.util.ByteList;
import org.jruby.util.DefinedMessage;
import org.jruby.util.JRubyClassLoader;
import org.jruby.util.SelfFirstJRubyClassLoader;
import org.jruby.util.IOInputStream;
import org.jruby.util.IOOutputStream;
import org.jruby.util.ClassDefininngJRubyClassLoader;
Expand Down Expand Up @@ -2582,7 +2583,12 @@ public static ClassLoader getClassLoader() {
public synchronized JRubyClassLoader getJRubyClassLoader() {
// FIXME: Get rid of laziness and handle restricted access elsewhere
if (!Ruby.isSecurityRestricted() && jrubyClassLoader == null) {
jrubyClassLoader = new JRubyClassLoader(config.getLoader());
if (config.isSelfFirstClassLoader()){
jrubyClassLoader = new SelfFirstJRubyClassLoader(config.getLoader());
}
else {
jrubyClassLoader = new JRubyClassLoader(config.getLoader());
}

// if jit code cache is used, we need to add the cache directory to the classpath
// so the previously generated class files can be reused.
Expand Down
24 changes: 24 additions & 0 deletions core/src/main/java/org/jruby/RubyInstanceConfig.java
Expand Up @@ -1227,6 +1227,28 @@ public boolean isNativeEnabled() {
return _nativeEnabled;
}

/**
* Set whether to use the self-first jruby classloader.
*
* @see Options#SELF_FIRST_CLASS_LOADER
*
* @param b new value indicating whether self-first classloader is used
*/
public void setSelfFirstClassLoader(boolean b) {
_selfFirstClassLoader = b;
}

/**
* Get whether to use the self-first jruby classloader.
*
* @see Options#SELF_FIRST_CLASS_LOADER
*
* @return true if self-first classloader is used; false otherwise.
*/
public boolean isSelfFirstClassLoader() {
return _selfFirstClassLoader;
}

/**
* @see Options#CLI_STRIP_HEADER
*/
Expand Down Expand Up @@ -1472,6 +1494,7 @@ public void setProfilingService( String service ) {
* Whether native code is enabled for this configuration.
*/
private boolean _nativeEnabled = NATIVE_ENABLED;
private boolean _selfFirstClassLoader = SELF_FIRST_CLASS_LOADER;

private TraceType traceType =
TraceType.traceTypeFor(Options.BACKTRACE_STYLE.load());
Expand Down Expand Up @@ -1674,6 +1697,7 @@ public boolean shouldPrecompileAll() {
* Set with the <tt>jruby.native.enabled</tt> system property.
*/
public static final boolean NATIVE_ENABLED = Options.NATIVE_ENABLED.load();
public static final boolean SELF_FIRST_CLASS_LOADER = Options.SELF_FIRST_CLASS_LOADER.load();

@Deprecated
public final static boolean CEXT_ENABLED = false;
Expand Down
22 changes: 0 additions & 22 deletions core/src/main/java/org/jruby/util/JRubyClassLoader.java
Expand Up @@ -120,28 +120,6 @@ public void addURL(URL url) {
indexJarContents(url);
}

@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
c = findClass(name);
} catch (ClassNotFoundException e) {
return super.loadClass(name, resolve);
}
}
return c;
}
}
public URL getResource( String name ) {
URL resource = findResource(name);
if (resource == null) {
resource = super.getResource(name);
}
return resource;
}

/**
* Called when the parent runtime is torn down.
*/
Expand Down
61 changes: 61 additions & 0 deletions core/src/main/java/org/jruby/util/SelfFirstJRubyClassLoader.java
@@ -0,0 +1,61 @@
/*
**** 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 SelfFirstJRubyClassLoader extends JRubyClassLoader {

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

@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
c = findClass(name);
} catch (ClassNotFoundException e) {
return super.loadClass(name, resolve);
}
}
return c;
}
}

@Override
public URL getResource( String name ) {
URL resource = findResource(name);
if (resource == null) {
resource = super.getResource(name);
}
return resource;
}
}
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/util/cli/Options.java
Expand Up @@ -169,6 +169,7 @@ public class Options {
public static final Option<Integer> THREADPOOL_TTL = integer(THREADPOOL, "thread.pool.ttl", 60, "The maximum number of seconds to keep alive an idle thread.");
public static final Option<Integer> FIBER_THREADPOOL_TTL = integer(THREADPOOL, "fiber.thread.pool.ttl", 60, "The maximum number of seconds to keep alive a pooled fiber thread.");

public static final Option<Boolean> SELF_FIRST_CLASS_LOADER = bool(MISCELLANEOUS, "self.first.class.loader", false, "Uses a self-first classloading strategy which might help in some cases of classloader conflicts.");

This comment has been minimized.

Copy link
@kares

kares Jan 28, 2015

Member

naming sounds a bit weird (maybe classloader.delegate true/false - it's named that way in libraries such as Tomcat), or considering there's a growing hierarchy of class-loaders these days (wanted?) it should be possible to switch them with a name specified as a string option e.g. classloader.type. alternatively delegate more work to the existing loader (I personally would welcome a delegate flag on JRubyClassLoader instead but I do not know the consequences) ...

This comment has been minimized.

Copy link
@mkristian

mkristian via email Jan 28, 2015

Author Member

This comment has been minimized.

Copy link
@kares

kares Jan 28, 2015

Member

I see, the obvious naming issue was that it did not start with classloader.xxx or loader.xxx so that if there are multiple JRuby class-loader options they share the same "name-space" ... also I probably would mostly switch it from an (embed) API not the command-line, but I might have missed a use-case, just wanted to give some feedback.

public static final Option<Boolean> OBJECTSPACE_ENABLED = bool(MISCELLANEOUS, "objectspace.enabled", false, "Enable or disable ObjectSpace.each_object.");
public static final Option<Boolean> SIPHASH_ENABLED = bool(MISCELLANEOUS, "siphash.enabled", false, "Enable or disable SipHash for String hash function.");
public static final Option<Boolean> LAUNCH_INPROC = bool(MISCELLANEOUS, "launch.inproc", false, "Set in-process launching of e.g. system('ruby ...').");
Expand Down
Expand Up @@ -22,8 +22,12 @@ public void java(){

@Test
public void ruby(){
System.setProperty( "jruby.self.first.class.loader", "true" );
ScriptingContainer container = new ScriptingContainer();
Object result = container.parse( "gem 'bouncy-castle-java', '1.5.0146.1'; require 'bouncy-castle-java'; Java::OrgBouncycastleJceProvider::BouncyCastleProvider.new.info").run();
assertEquals( "BouncyCastle Security Provider v1.46", result.toString() );

result = container.parse( "JRuby.runtime.jruby_class_loader").run();
assertEquals( "org.jruby.util.SelfFirstJRubyClassLoader", result.toString().replaceFirst( "@.*$", "" ) );
}
}
2 changes: 1 addition & 1 deletion maven/jruby-complete/src/it/bouncycastle/pom.xml
Expand Up @@ -33,7 +33,7 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<bc.version>1.49</bc.version>
<bc.version>1.47</bc.version>
</properties>

<build>
Expand Down
Expand Up @@ -10,13 +10,17 @@
public class BouncyCastleTestCase {
@Test
public void java(){
assertEquals( "BouncyCastle Security Provider v1.49", new BouncyCastleProvider().getInfo() );
assertEquals( "BouncyCastle Security Provider v1.47", new BouncyCastleProvider().getInfo() );
}

@Test
public void ruby(){
System.setProperty( "jruby.self.first.class.loader", "true" );
ScriptingContainer container = new ScriptingContainer();
Object result = container.parse( "require 'openssl'; Java::OrgBouncycastleJceProvider::BouncyCastleProvider.new.info").run();
assertEquals( "BouncyCastle Security Provider v1.47", result.toString() );
assertEquals( "BouncyCastle Security Provider v1.49", result.toString() );

result = container.parse( "JRuby.runtime.jruby_class_loader").run();
assertEquals( "org.jruby.util.SelfFirstJRubyClassLoader", result.toString().replaceFirst( "@.*$", "" ) );
}
}
Expand Up @@ -22,8 +22,12 @@ public void java(){

@Test
public void ruby(){
System.setProperty( "jruby.self.first.class.loader", "true" );
ScriptingContainer container = new ScriptingContainer();
Object result = container.parse( "gem 'bouncy-castle-java'; require 'bouncy-castle-java'; Java::OrgBouncycastleJceProvider::BouncyCastleProvider.new.info").run();
assertEquals( "BouncyCastle Security Provider v1.47", result.toString() );
Object result = container.parse( "gem 'bouncy-castle-java', '1.5.0146.1'; require 'bouncy-castle-java'; Java::OrgBouncycastleJceProvider::BouncyCastleProvider.new.info").run();
assertEquals( "BouncyCastle Security Provider v1.46", result.toString() );

result = container.parse( "JRuby.runtime.jruby_class_loader").run();
assertEquals( "org.jruby.util.SelfFirstJRubyClassLoader", result.toString().replaceFirst( "@.*$", "" ) );
}
}
Expand Up @@ -27,8 +27,12 @@ public void java(){

@Test
public void ruby(){
System.setProperty( "jruby.self.first.class.loader", "true" );
ScriptingContainer container = new ScriptingContainer();
Object result = container.parse( "require 'openssl'; Java::OrgBouncycastleJceProvider::BouncyCastleProvider.new.info").run();
assertEquals( "BouncyCastle Security Provider v1.49", result.toString() );

result = container.parse( "JRuby.runtime.jruby_class_loader").run();
assertEquals( "org.jruby.util.SelfFirstJRubyClassLoader", result.toString().replaceFirst( "@.*$", "" ) );
}
}

0 comments on commit 580bd4e

Please sign in to comment.