diff --git a/core/src/main/java/org/jruby/Ruby.java b/core/src/main/java/org/jruby/Ruby.java index 647cf199387..6ec1c10242f 100644 --- a/core/src/main/java/org/jruby/Ruby.java +++ b/core/src/main/java/org/jruby/Ruby.java @@ -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; @@ -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. diff --git a/core/src/main/java/org/jruby/RubyInstanceConfig.java b/core/src/main/java/org/jruby/RubyInstanceConfig.java index e28d12dea53..6ebf793d4e7 100644 --- a/core/src/main/java/org/jruby/RubyInstanceConfig.java +++ b/core/src/main/java/org/jruby/RubyInstanceConfig.java @@ -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 */ @@ -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()); @@ -1674,6 +1697,7 @@ public boolean shouldPrecompileAll() { * Set with the jruby.native.enabled 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; diff --git a/core/src/main/java/org/jruby/util/JRubyClassLoader.java b/core/src/main/java/org/jruby/util/JRubyClassLoader.java index 0db783dae69..2d5e0b5f230 100644 --- a/core/src/main/java/org/jruby/util/JRubyClassLoader.java +++ b/core/src/main/java/org/jruby/util/JRubyClassLoader.java @@ -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. */ diff --git a/core/src/main/java/org/jruby/util/SelfFirstJRubyClassLoader.java b/core/src/main/java/org/jruby/util/SelfFirstJRubyClassLoader.java new file mode 100644 index 00000000000..03727c8d124 --- /dev/null +++ b/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; + } +} diff --git a/core/src/main/java/org/jruby/util/cli/Options.java b/core/src/main/java/org/jruby/util/cli/Options.java index 771118d12d1..f1e89b586b6 100644 --- a/core/src/main/java/org/jruby/util/cli/Options.java +++ b/core/src/main/java/org/jruby/util/cli/Options.java @@ -169,6 +169,7 @@ public class Options { public static final Option THREADPOOL_TTL = integer(THREADPOOL, "thread.pool.ttl", 60, "The maximum number of seconds to keep alive an idle thread."); public static final Option 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 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."); public static final Option OBJECTSPACE_ENABLED = bool(MISCELLANEOUS, "objectspace.enabled", false, "Enable or disable ObjectSpace.each_object."); public static final Option SIPHASH_ENABLED = bool(MISCELLANEOUS, "siphash.enabled", false, "Enable or disable SipHash for String hash function."); public static final Option LAUNCH_INPROC = bool(MISCELLANEOUS, "launch.inproc", false, "Set in-process launching of e.g. system('ruby ...')."); diff --git a/maven/jruby-complete/src/it/bouncycastle-with-bc-gem/src/test/java/org/jruby/its/BouncyCastleTestCase.java b/maven/jruby-complete/src/it/bouncycastle-with-bc-gem/src/test/java/org/jruby/its/BouncyCastleTestCase.java index d78ff65e582..e25c9cd3c47 100644 --- a/maven/jruby-complete/src/it/bouncycastle-with-bc-gem/src/test/java/org/jruby/its/BouncyCastleTestCase.java +++ b/maven/jruby-complete/src/it/bouncycastle-with-bc-gem/src/test/java/org/jruby/its/BouncyCastleTestCase.java @@ -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( "@.*$", "" ) ); } } diff --git a/maven/jruby-complete/src/it/bouncycastle/pom.xml b/maven/jruby-complete/src/it/bouncycastle/pom.xml index 9fc13322869..52a6f5a3c5e 100644 --- a/maven/jruby-complete/src/it/bouncycastle/pom.xml +++ b/maven/jruby-complete/src/it/bouncycastle/pom.xml @@ -33,7 +33,7 @@ UTF-8 - 1.49 + 1.47 diff --git a/maven/jruby-complete/src/it/bouncycastle/src/test/java/org/jruby/its/BouncyCastleTestCase.java b/maven/jruby-complete/src/it/bouncycastle/src/test/java/org/jruby/its/BouncyCastleTestCase.java index 5fe8cca0b8a..05d883d27d8 100644 --- a/maven/jruby-complete/src/it/bouncycastle/src/test/java/org/jruby/its/BouncyCastleTestCase.java +++ b/maven/jruby-complete/src/it/bouncycastle/src/test/java/org/jruby/its/BouncyCastleTestCase.java @@ -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( "@.*$", "" ) ); } } diff --git a/maven/jruby/src/it/bouncycastle-with-bc-gem/src/test/java/org/jruby/its/BouncyCastleTestCase.java b/maven/jruby/src/it/bouncycastle-with-bc-gem/src/test/java/org/jruby/its/BouncyCastleTestCase.java index 752212366a1..85e355b59c3 100644 --- a/maven/jruby/src/it/bouncycastle-with-bc-gem/src/test/java/org/jruby/its/BouncyCastleTestCase.java +++ b/maven/jruby/src/it/bouncycastle-with-bc-gem/src/test/java/org/jruby/its/BouncyCastleTestCase.java @@ -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( "@.*$", "" ) ); } } diff --git a/maven/jruby/src/it/bouncycastle/src/test/java/org/jruby/its/BouncyCastleTestCase.java b/maven/jruby/src/it/bouncycastle/src/test/java/org/jruby/its/BouncyCastleTestCase.java index 70c6df0f290..f2bcab2b7b1 100644 --- a/maven/jruby/src/it/bouncycastle/src/test/java/org/jruby/its/BouncyCastleTestCase.java +++ b/maven/jruby/src/it/bouncycastle/src/test/java/org/jruby/its/BouncyCastleTestCase.java @@ -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( "@.*$", "" ) ); } }