From 70ee2012e901214e98c5c24ccf42c8a3af2b7647 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Thu, 7 Apr 2016 00:07:38 +0200 Subject: [PATCH] Revert "[FIXED JENKINS-21486] Merged #2172: enforce plugin dependencies." This reverts commit fa39668b814a6c51e7c460f529692b149e835fc0, reversing changes made to be9bc0dba552e271b47102292fc9f585afaaf212. --- changelog.html | 5 +- core/src/main/java/hudson/PluginManager.java | 3 +- core/src/main/java/hudson/PluginWrapper.java | 98 ++---------------- .../test/java/hudson/PluginManagerTest.java | 74 ------------- .../test/java/hudson/PluginManagerUtil.java | 7 -- .../test/java/hudson/PluginWrapperTest.java | 26 ----- .../test/resources/plugins/dependee-0.0.2.hpi | Bin 4671 -> 0 bytes .../test/resources/plugins/depender-0.0.2.hpi | Bin 5500 -> 0 bytes .../plugins/mandatory-depender-0.0.2.hpi | Bin 5587 -> 0 bytes 9 files changed, 12 insertions(+), 201 deletions(-) delete mode 100644 test/src/test/java/hudson/PluginWrapperTest.java delete mode 100644 test/src/test/resources/plugins/dependee-0.0.2.hpi delete mode 100644 test/src/test/resources/plugins/depender-0.0.2.hpi delete mode 100644 test/src/test/resources/plugins/mandatory-depender-0.0.2.hpi diff --git a/changelog.html b/changelog.html index 910693ef0b..a88d49efae 100644 --- a/changelog.html +++ b/changelog.html @@ -56,10 +56,7 @@ Upcoming changes

What's new in 1.656 (2016/04/03)

diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 906f44fd7a..52e6e5ba13 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -679,8 +679,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas // so existing plugins can't be depending on this newly deployed one. plugins.add(p); - if (p.isActive()) - activePlugins.add(p); + activePlugins.add(p); synchronized (((UberClassLoader) uberClassLoader).loaded) { ((UberClassLoader) uberClassLoader).loaded.clear(); } diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index e5ca24513d..c30dba8ae2 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -87,12 +87,6 @@ import javax.annotation.Nonnull; */ @ExportedBean public class PluginWrapper implements Comparable, ModelObject { - /** - * A plugin won't be loaded unless his declared dependencies are present and match the required minimal version. - * This can be set to false to disable the version check (legacy behaviour) - */ - private static final boolean ENABLE_PLUGIN_DEPENDENCIES_VERSION_CHECK = Boolean.parseBoolean(System.getProperty(PluginWrapper.class.getName()+"." + "dependenciesVersionCheck.enabled", "true")); - /** * {@link PluginManager} to which this belongs to. */ @@ -214,10 +208,10 @@ public class PluginWrapper implements Comparable, ModelObject { if(idx==-1) throw new IllegalArgumentException("Illegal dependency specifier "+s); this.shortName = s.substring(0,idx); - String version = s.substring(idx+1); - + this.version = s.substring(idx+1); + boolean isOptional = false; - String[] osgiProperties = version.split("[;]"); + String[] osgiProperties = s.split(";"); for (int i = 1; i < osgiProperties.length; i++) { String osgiProperty = osgiProperties[i].trim(); if (osgiProperty.equalsIgnoreCase("resolution:=optional")) { @@ -225,16 +219,11 @@ public class PluginWrapper implements Comparable, ModelObject { } } this.optional = isOptional; - if (isOptional) { - this.version = osgiProperties[0]; - } else { - this.version = version; - } } @Override public String toString() { - return shortName + " (" + version + ") " + (optional ? "optional" : ""); + return shortName + " (" + version + ")"; } } @@ -398,21 +387,6 @@ public class PluginWrapper implements Comparable, ModelObject { return "???"; } - /** - * Returns the required Jenkins core version of this plugin. - * @return the required Jenkins core version of this plugin. - * @since 1.657 - */ - @Exported - public @CheckForNull String getRequiredCoreVersion() { - String v = manifest.getMainAttributes().getValue("Jenkins-Version"); - if (v!= null) return v; - - v = manifest.getMainAttributes().getValue("Hudson-Version"); - if (v!= null) return v; - return null; - } - /** * Returns the version number of this plugin */ @@ -539,71 +513,19 @@ public class PluginWrapper implements Comparable, ModelObject { * thrown if one or several mandatory dependencies doesn't exists. */ /*package*/ void resolvePluginDependencies() throws IOException { - if (ENABLE_PLUGIN_DEPENDENCIES_VERSION_CHECK) { - String requiredCoreVersion = getRequiredCoreVersion(); - if (requiredCoreVersion == null) { - LOGGER.warning(shortName + " doesn't declare required core version."); - } else { - if (Jenkins.getVersion().isOlderThan(new VersionNumber(requiredCoreVersion))) { - throw new IOException(shortName + " requires a more recent core version (" + requiredCoreVersion + ") than the current (" + Jenkins.getVersion() + ")."); - } - } - } List missingDependencies = new ArrayList(); - List obsoleteDependencies = new ArrayList(); - List disabledDependencies = new ArrayList(); // make sure dependencies exist for (Dependency d : dependencies) { - PluginWrapper dependency = parent.getPlugin(d.shortName); - if (dependency == null) { + if (parent.getPlugin(d.shortName) == null) missingDependencies.add(d.toString()); - } else { - if (dependency.isActive()) { - if (ENABLE_PLUGIN_DEPENDENCIES_VERSION_CHECK && dependency.getVersionNumber().isOlderThan(new VersionNumber(d.version))) { - obsoleteDependencies.add(dependency.getShortName() + "(" + dependency.getVersion() + " < " + d.version + ")"); - } - } else { - disabledDependencies.add(d.toString()); - } - - } } + if (!missingDependencies.isEmpty()) + throw new IOException("Dependency "+Util.join(missingDependencies, ", ")+" doesn't exist"); + // add the optional dependencies that exists for (Dependency d : optionalDependencies) { - PluginWrapper dependency = parent.getPlugin(d.shortName); - if (dependency != null && dependency.isActive()) { - if (ENABLE_PLUGIN_DEPENDENCIES_VERSION_CHECK && dependency.getVersionNumber().isOlderThan(new VersionNumber(d.version))) { - obsoleteDependencies.add(dependency.getShortName() + "(" + dependency.getVersion() + " < " + d.version + ")"); - } else { - dependencies.add(d); - } - } - } - StringBuilder messageBuilder = new StringBuilder(); - if (!missingDependencies.isEmpty()) { - boolean plural = missingDependencies.size() > 1; - messageBuilder.append(plural ? "Dependencies " : "Dependency ") - .append(Util.join(missingDependencies, ", ")) - .append(" ").append(plural ? "don't" : "doesn't") - .append(" exist. "); - } - if (!disabledDependencies.isEmpty()) { - boolean plural = disabledDependencies.size() > 1; - messageBuilder.append(plural ? "Dependencies " : "Dependency ") - .append(Util.join(missingDependencies, ", ")) - .append(" ").append(plural ? "are" : "is") - .append(" disabled. "); - } - if (!obsoleteDependencies.isEmpty()) { - boolean plural = obsoleteDependencies.size() > 1; - messageBuilder.append(plural ? "Dependencies " : "Dependency ") - .append(Util.join(obsoleteDependencies, ", ")) - .append(" ").append(plural ? "are" : "is") - .append(" older than required."); - } - String message = messageBuilder.toString(); - if (!message.isEmpty()) { - throw new IOException(message); + if (parent.getPlugin(d.shortName) != null) + dependencies.add(d); } } diff --git a/test/src/test/java/hudson/PluginManagerTest.java b/test/src/test/java/hudson/PluginManagerTest.java index 777bf4d23f..913e3f4a15 100644 --- a/test/src/test/java/hudson/PluginManagerTest.java +++ b/test/src/test/java/hudson/PluginManagerTest.java @@ -47,7 +47,6 @@ import net.sf.json.JSONObject; import org.apache.commons.io.FileUtils; import org.apache.tools.ant.filters.StringInputStream; import static org.junit.Assert.*; - import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -340,75 +339,6 @@ public class PluginManagerTest { assertTrue(r.jenkins.getExtensionList("org.jenkinsci.plugins.dependencytest.dependee.DependeeExtensionPoint").isEmpty()); } - @Issue("JENKINS-21486") - @Test public void installPluginWithObsoleteDependencyFails() throws Exception { - // Load dependee 0.0.1. - { - dynamicLoad("dependee.hpi"); - } - - // Load mandatory-depender 0.0.2, depending on dependee 0.0.2 - try { - dynamicLoad("mandatory-depender-0.0.2.hpi"); - fail("Should not have worked"); - } catch (IOException e) { - // Expected - } - } - - @Issue("JENKINS-21486") - @Test public void installPluginWithDisabledOptionalDependencySucceeds() throws Exception { - // Load dependee 0.0.2. - { - dynamicLoadAndDisable("dependee-0.0.2.hpi"); - } - - // Load depender 0.0.2, depending optionally on dependee 0.0.2 - { - dynamicLoad("depender-0.0.2.hpi"); - } - - // dependee is not loaded so we cannot list any extension for it. - try { - r.jenkins.getExtensionList("org.jenkinsci.plugins.dependencytest.dependee.DependeeExtensionPoint"); - fail(); - } catch( ClassNotFoundException _ ){ - } - } - - @Issue("JENKINS-21486") - @Test public void installPluginWithDisabledDependencyFails() throws Exception { - // Load dependee 0.0.2. - { - dynamicLoadAndDisable("dependee-0.0.2.hpi"); - } - - // Load mandatory-depender 0.0.2, depending on dependee 0.0.2 - try { - dynamicLoad("mandatory-depender-0.0.2.hpi"); - fail("Should not have worked"); - } catch (IOException e) { - // Expected - } - } - - - @Issue("JENKINS-21486") - @Test public void installPluginWithObsoleteOptionalDependencyFails() throws Exception { - // Load dependee 0.0.1. - { - dynamicLoad("dependee.hpi"); - } - - // Load depender 0.0.2, depending optionally on dependee 0.0.2 - try { - dynamicLoad("depender-0.0.2.hpi"); - fail("Should not have worked"); - } catch (IOException e) { - // Expected - } - } - @Issue("JENKINS-12753") @WithPlugin("tasks.jpi") @Test public void dynamicLoadRestartRequiredException() throws Exception { @@ -447,8 +377,4 @@ public class PluginManagerTest { private void dynamicLoad(String plugin) throws IOException, InterruptedException, RestartRequiredException { PluginManagerUtil.dynamicLoad(plugin, r.jenkins); } - - private void dynamicLoadAndDisable(String plugin) throws IOException, InterruptedException, RestartRequiredException { - PluginManagerUtil.dynamicLoad(plugin, r.jenkins, true); - } } diff --git a/test/src/test/java/hudson/PluginManagerUtil.java b/test/src/test/java/hudson/PluginManagerUtil.java index 2263622b4c..e67b9b31f9 100644 --- a/test/src/test/java/hudson/PluginManagerUtil.java +++ b/test/src/test/java/hudson/PluginManagerUtil.java @@ -48,16 +48,9 @@ public class PluginManagerUtil { } public static void dynamicLoad(String plugin, Jenkins jenkins) throws IOException, InterruptedException, RestartRequiredException { - dynamicLoad(plugin, jenkins, false); - } - - public static void dynamicLoad(String plugin, Jenkins jenkins, boolean disable) throws IOException, InterruptedException, RestartRequiredException { URL src = PluginManagerTest.class.getClassLoader().getResource("plugins/" + plugin); File dest = new File(jenkins.getRootDir(), "plugins/" + plugin); FileUtils.copyURLToFile(src, dest); - if (disable) { - new File(dest.getPath() + ".disabled").createNewFile(); - } jenkins.pluginManager.dynamicLoad(dest); } } diff --git a/test/src/test/java/hudson/PluginWrapperTest.java b/test/src/test/java/hudson/PluginWrapperTest.java deleted file mode 100644 index 7529c53194..0000000000 --- a/test/src/test/java/hudson/PluginWrapperTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package hudson; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class PluginWrapperTest { - - @Test - public void dependencyTest() { - String version = "plugin:0.0.2"; - PluginWrapper.Dependency dependency = new PluginWrapper.Dependency(version); - assertEquals("plugin", dependency.shortName); - assertEquals("0.0.2", dependency.version); - assertEquals(false, dependency.optional); - } - - @Test - public void optionalDependencyTest() { - String version = "plugin:0.0.2;resolution:=optional"; - PluginWrapper.Dependency dependency = new PluginWrapper.Dependency(version); - assertEquals("plugin", dependency.shortName); - assertEquals("0.0.2", dependency.version); - assertEquals(true, dependency.optional); - } -} diff --git a/test/src/test/resources/plugins/dependee-0.0.2.hpi b/test/src/test/resources/plugins/dependee-0.0.2.hpi deleted file mode 100644 index 79525b08464fdfe9ef41c5e7f18b4ab3afb78705..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4671 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