diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index fe6ea4205eb6bd7a482011a540dc9f5dd7398b0b..0970e1c07546e17f10d313bc90fda992287f7000 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -289,8 +289,8 @@ public class ClassicPluginStrategy implements PluginStrategy { } public void load(PluginWrapper wrapper) throws IOException { - // override the context classloader so that XStream activity in plugin.start() - // will be able to resolve classes in this plugin + // override the context classloader. This no longer makes sense, + // but it is left for the backward compatibility ClassLoader old = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(wrapper.classLoader); try { diff --git a/core/src/main/java/hudson/LocalPluginManager.java b/core/src/main/java/hudson/LocalPluginManager.java index 2b96c7dcccd92d5d7f074d46472004b413ab08cc..184ce191c5e01299d3054c238be9026b67c3ca9d 100644 --- a/core/src/main/java/hudson/LocalPluginManager.java +++ b/core/src/main/java/hudson/LocalPluginManager.java @@ -26,6 +26,7 @@ package hudson; import jenkins.model.Jenkins; +import javax.servlet.ServletContext; import java.io.File; import java.io.IOException; import java.net.URL; @@ -42,10 +43,12 @@ import java.util.logging.Logger; * @author Kohsuke Kawaguchi */ public class LocalPluginManager extends PluginManager { - private final Jenkins jenkins; public LocalPluginManager(Jenkins jenkins) { super(jenkins.servletContext, new File(jenkins.getRootDir(),"plugins")); - this.jenkins = jenkins; + } + + public LocalPluginManager(File rootDir) { + super(null, new File(rootDir,"plugins")); } /** @@ -63,7 +66,9 @@ public class LocalPluginManager extends PluginManager { Set names = new HashSet(); - for( String path : Util.fixNull((Set) jenkins.servletContext.getResourcePaths("/WEB-INF/plugins"))) { + ServletContext context = Jenkins.getInstance().servletContext; + + for( String path : Util.fixNull((Set)context.getResourcePaths("/WEB-INF/plugins"))) { String fileName = path.substring(path.lastIndexOf('/')+1); if(fileName.length()==0) { // see http://www.nabble.com/404-Not-Found-error-when-clicking-on-help-td24508544.html @@ -73,7 +78,7 @@ public class LocalPluginManager extends PluginManager { try { names.add(fileName); - URL url = jenkins.servletContext.getResource(path); + URL url = context.getResource(path); copyBundledPlugin(url, fileName); } catch (IOException e) { LOGGER.log(Level.SEVERE, "Failed to extract the bundled plugin "+fileName,e); diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index c384f0b56c038ce1a04ff08e23a6c50ce82d1fd2..156a38d43e105e25fbb1af2d33227628321dd7dc 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -358,7 +358,7 @@ public abstract class PluginManager extends AbstractModelObject { /** * Creates a hudson.PluginStrategy, looking at the corresponding system property. */ - private PluginStrategy createPluginStrategy() { + protected PluginStrategy createPluginStrategy() { String strategyName = System.getProperty(PluginStrategy.class.getName()); if (strategyName != null) { try { @@ -635,16 +635,6 @@ public abstract class PluginManager extends AbstractModelObject { else generatedClasses.remove(name,wc); } - // first, use the context classloader so that plugins that are loading - // can use its own classloader first. - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - if(cl!=null && cl!=this) - try { - return cl.loadClass(name); - } catch(ClassNotFoundException e) { - // not found. try next - } - for (PluginWrapper p : activePlugins) { try { return p.classLoader.loadClass(name); diff --git a/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java b/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java index 33700610b15bad60bacc8cade9a0aaf331b3705a..3581b83f84b467e8f76847c78347a7304710e67b 100644 --- a/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java +++ b/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java @@ -244,11 +244,20 @@ public abstract class HudsonTestCase extends TestCase implements RootAction { * This will cause a fresh {@link PluginManager} to be created for this test. * Leaving this to false enables the test harness to use a pre-loaded plugin manager, * which runs faster. + * + * @deprecated + * Use {@link #pluginManager} */ public boolean useLocalPluginManager; - public ComputerConnectorTester computerConnectorTester = new ComputerConnectorTester(this); + /** + * Set the plugin manager to be passed to {@link Jenkins} constructor. + * + * For historical reasons, {@link #useLocalPluginManager}==true will take the precedence. + */ + private PluginManager pluginManager = TestPluginManager.INSTANCE; + public ComputerConnectorTester computerConnectorTester = new ComputerConnectorTester(this); protected HudsonTestCase(String name) { super(name); @@ -313,6 +322,8 @@ public abstract class HudsonTestCase extends TestCase implements RootAction { d.load(); } + + /** * Configures the update center setting for the test. * By default, we load updates from local proxy to avoid network traffic as much as possible. @@ -411,7 +422,20 @@ public abstract class HudsonTestCase extends TestCase implements RootAction { File home = homeLoader.allocate(); for (Runner r : recipes) r.decorateHome(this,home); - return new Hudson(home, createWebServer(), useLocalPluginManager ? null : TestPluginManager.INSTANCE); + return new Hudson(home, createWebServer(), useLocalPluginManager ? null : pluginManager); + } + + /** + * Sets the {@link PluginManager} to be used when creating a new {@link Jenkins} instance. + * + * @param pluginManager + * null to let Jenkins create a new instance of default plugin manager, like it normally does when running as a webapp outside the test. + */ + public void setPluginManager(PluginManager pluginManager) { + this.useLocalPluginManager = false; + this.pluginManager = pluginManager; + if (hudson!=null) + throw new IllegalStateException("Too late to override the plugin manager"); } /** diff --git a/test/src/main/java/org/jvnet/hudson/test/recipes/WithPluginManager.java b/test/src/main/java/org/jvnet/hudson/test/recipes/WithPluginManager.java new file mode 100644 index 0000000000000000000000000000000000000000..84682c8e8c868b3be3381ccb6239195405f49b63 --- /dev/null +++ b/test/src/main/java/org/jvnet/hudson/test/recipes/WithPluginManager.java @@ -0,0 +1,81 @@ +/* + * The MIT License + * + * Copyright (c) 2011, CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jvnet.hudson.test.recipes; + +import hudson.PluginManager; +import org.jvnet.hudson.test.HudsonTestCase; + +import java.io.File; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.lang.reflect.Constructor; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +/** + * Runs the test case with a custom plugin manager. + * + * @author Kohsuke Kawaguchi + */ +@Documented +@Recipe(WithPluginManager.RunnerImpl.class) +@Target(METHOD) +@Retention(RUNTIME) +public @interface WithPluginManager { + Class value(); + + public class RunnerImpl extends Recipe.Runner { + private WithPluginManager recipe; + @Override + public void setup(HudsonTestCase testCase, WithPluginManager recipe) throws Exception { + this.recipe = recipe; + } + + @Override + public void decorateHome(HudsonTestCase testCase, File home) throws Exception { + Class c = recipe.value(); + Constructor ctr = c.getConstructors()[0]; + + // figure out parameters + Class[] pt = ctr.getParameterTypes(); + Object[] args = new Object[pt.length]; + for (int i=0; i