diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 58a419b568..e0914220d4 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -2239,7 +2239,9 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } /** - * {@link AdministrativeMonitor} that checks if there are any plugins that is deprecated. + * {@link AdministrativeMonitor} that checks if there are any plugins that are deprecated. + * + * @since TODO */ @Restricted(NoExternalUse.class) @Symbol("pluginDeprecation") @@ -2251,22 +2253,14 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas return Messages.PluginManager_PluginDeprecationMonitor_DisplayName(); } - private transient volatile boolean isActive = false; - - private transient volatile List deprecatedPlugins; - public boolean isActivated() { - if (deprecatedPlugins == null) { - deprecatedPlugins = Jenkins.get().getPluginManager().getPlugins().stream() - .filter(PluginWrapper::isDeprecated) - .collect(Collectors.toList()); - isActive = !deprecatedPlugins.isEmpty(); - } - return isActive; + return !getDeprecatedPlugins().isEmpty(); } public List getDeprecatedPlugins() { - return deprecatedPlugins; + return Jenkins.get().getPluginManager().getPlugins().stream() + .filter(PluginWrapper::isDeprecated) + .collect(Collectors.toList()); } } diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index 4084f43488..32c67e771d 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -365,12 +365,18 @@ public class PluginWrapper implements Comparable, ModelObject { } /** - * Is this plugin labeled deprecated? - * @return {@code true} when plugin contains deprecated in categories, otherwise {@code false}. + * Is this plugin deprecated? + * + * @return {@code true} if and only if an update site reports deprecations for this plugin. * @since TODO */ + @Restricted(NoExternalUse.class) public boolean isDeprecated() { - return getInfo().isDeprecated(); + /* + While better handled in UpdateSite.Plugin that only exists for plugins actively being published. + We have no good model for plugin metadata from update sites for plugins not being published. + */ + return !getDeprecations().isEmpty(); } @ExportedBean @@ -562,8 +568,11 @@ public class PluginWrapper implements Comparable, ModelObject { /** * Returns a one-line descriptive name of this plugin. + * + * @deprecated For most purposes, use {@link #getDisplayName()}. */ @Exported + @Deprecated public String getLongName() { String name = manifest.getMainAttributes().getValue("Long-Name"); if(name!=null) return name; @@ -1293,6 +1302,20 @@ public class PluginWrapper implements Comparable, ModelObject { return ExtensionList.lookupSingleton(UpdateSiteWarningsMonitor.class).getActivePluginWarningsByPlugin().getOrDefault(this, Collections.emptyList()); } + @Restricted(NoExternalUse.class) + public Set getDeprecations() { + /* Would be much nicer to go through getInfoFromAllSites but that only works for currently published plugins */ + Set deprecations = new HashSet<>(); + for (UpdateSite site : Jenkins.get().getUpdateCenter().getSites()) { + for (Map.Entry entry : site.getData().getDeprecations().entrySet()) { + if (entry.getKey().equals(this.shortName)) { + deprecations.add(entry.getValue()); + } + } + } + return deprecations; + } + private static final Logger LOGGER = Logger.getLogger(PluginWrapper.class.getName()); /** diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java index d143a539ba..84994570cb 100644 --- a/core/src/main/java/hudson/model/UpdateCenter.java +++ b/core/src/main/java/hudson/model/UpdateCenter.java @@ -976,10 +976,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas Map pluginMap = new LinkedHashMap<>(); for (UpdateSite site : sites) { for (Plugin plugin: site.getAvailables()) { - if (plugin.isDeprecated()) { - // hide plugins that are deprecated - continue; - } final Plugin existing = pluginMap.get(plugin.name); if (existing == null) { pluginMap.put(plugin.name, plugin); diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index 3af8ecd2bc..7f0acaf9e7 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -49,10 +49,13 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.UUID; @@ -556,6 +559,13 @@ public class UpdateSite { */ private final Set warnings = new HashSet<>(); + /** + * Mapping of plugin IDs to deprecation notices + * + * @since TODO + */ + private final Map deprecations = new HashMap<>(); + /** * If this is non-null, Jenkins is going to check the connectivity to this URL to make sure * the network connection is up. Null to skip the check. @@ -582,6 +592,19 @@ public class UpdateSite { } } + JSONObject deprecations = o.optJSONObject("deprecations"); + if (deprecations != null) { + for (Iterator it = deprecations.keys(); it.hasNext(); ) { + try { + String pluginId = it.next().toString(); + String referenceUrl = deprecations.getString(pluginId); + this.deprecations.put(pluginId, new Deprecation(referenceUrl)); + } catch (JSONException ex) { + LOGGER.log(Level.WARNING, "Failed to parse JSON for deprecation", ex); + } + } + } + for(Map.Entry e : (Set>)o.getJSONObject("plugins").entrySet()) { Plugin p = new Plugin(sourceId, e.getValue()); // JENKINS-33308 - include implied dependencies for older plugins that may need them @@ -594,6 +617,14 @@ public class UpdateSite { } } plugins.put(Util.intern(e.getKey()), p); + + // compatibility with update sites that have no separate 'deprecated' top-level entry. + // Also do this even if there are deprecations to potentially allow limiting the top-level entry to overridden URLs. + if (Arrays.asList(p.categories).contains("deprecated")) { + if (!this.deprecations.containsKey(p.name)) { + this.deprecations.put(p.name, new Deprecation(p.wiki)); + } + } } connectionCheckUrl = (String)o.get("connectionCheckUrl"); @@ -609,6 +640,16 @@ public class UpdateSite { return this.warnings; } + /** + * Returns the deprecations provided by the update site + * @return the deprecations provided by the update site + * @since TODO + */ + @Restricted(NoExternalUse.class) + public Map getDeprecations() { + return this.deprecations; + } + /** * Is there a new version of the core? */ @@ -791,6 +832,32 @@ public class UpdateSite { } } + /** + * Represents a deprecation of a certain component. Jenkins project policy determines exactly what it means. + * + * @since TODO + */ + @Restricted(NoExternalUse.class) + public static final class Deprecation { + public final String url; + public Deprecation(String url) { + this.url = url; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Deprecation that = (Deprecation) o; + return Objects.equals(url, that.url); + } + + @Override + public int hashCode() { + return Objects.hash(url); + } + } + /** * Represents a warning about a certain component, mostly related to known security issues. * @@ -1099,7 +1166,15 @@ public class UpdateSite { } + @Restricted(NoExternalUse.class) + public boolean isDeprecated() { + return getDeprecation() != null; + } + @Restricted(NoExternalUse.class) + public UpdateSite.Deprecation getDeprecation() { + return Jenkins.get().getUpdateCenter().getSite(sourceId).getData().getDeprecations().get(this.name); + } public String getDisplayName() { String displayName; @@ -1414,15 +1489,6 @@ public class UpdateSite { return warnings; } - /** - * Is this plugin labeled deprecated? - * @return {@code true} when plugin contains deprecated in categories, otherwise {@code false}. - * @since TODO - */ - public boolean isDeprecated() { - return categories != null && Arrays.asList(categories).contains("deprecated"); - } - /** * @since 2.40 */ diff --git a/core/src/main/resources/hudson/PluginManager/PluginDeprecationMonitor/message.jelly b/core/src/main/resources/hudson/PluginManager/PluginDeprecationMonitor/message.jelly index fdd14ef538..5e40379e33 100644 --- a/core/src/main/resources/hudson/PluginManager/PluginDeprecationMonitor/message.jelly +++ b/core/src/main/resources/hudson/PluginManager/PluginDeprecationMonitor/message.jelly @@ -27,13 +27,13 @@ THE SOFTWARE.
${%DeprecatedPlugins}
- + -
${p.longName} ${p.version}
+
${p.displayName}
-
${p.longName} ${p.version}
+
${p.displayName}}
diff --git a/core/src/main/resources/hudson/PluginManager/PluginDeprecationMonitor/message.properties b/core/src/main/resources/hudson/PluginManager/PluginDeprecationMonitor/message.properties index 837b2baad1..94a77a83dc 100644 --- a/core/src/main/resources/hudson/PluginManager/PluginDeprecationMonitor/message.properties +++ b/core/src/main/resources/hudson/PluginManager/PluginDeprecationMonitor/message.properties @@ -24,4 +24,4 @@ DeprecatedPlugins=There are deprecated plugins installed DeprecatedPluginsDescription=\ In general, this means that these plugins are either obsolete, no longer being developed, or may no longer work.
\ - See the plugin's documentation for further information about the cause for the deprecation, and suggestions on how to proceed. + See the linked web pages for further information about the cause for the deprecation, and suggestions on how to proceed. diff --git a/core/src/main/resources/hudson/PluginManager/installed.jelly b/core/src/main/resources/hudson/PluginManager/installed.jelly index 6291241e99..e8b169de09 100644 --- a/core/src/main/resources/hudson/PluginManager/installed.jelly +++ b/core/src/main/resources/hudson/PluginManager/installed.jelly @@ -133,8 +133,8 @@ THE SOFTWARE.
-
- ${%deprecationWarning} +
+ ${%deprecationWarning(p.getDeprecations().get(0).url)}
diff --git a/core/src/main/resources/hudson/PluginManager/installed.properties b/core/src/main/resources/hudson/PluginManager/installed.properties index 5905200350..15f4137a46 100644 --- a/core/src/main/resources/hudson/PluginManager/installed.properties +++ b/core/src/main/resources/hudson/PluginManager/installed.properties @@ -24,10 +24,7 @@ requires.restart=This Jenkins instance requires a restart. Changing the state of detached-disable=This plugin may not be safe to disable detached-uninstall=This plugin may not be safe to uninstall detached-possible-dependents=Its functionality was at one point moved out of Jenkins core, and another plugin with a core dependency predating the split may be relying on it implicitly. -deprecationWarning=\ - Warning: This plugin has been marked as deprecated.
\ - In general, this means that this plugin is either obsolete, no longer being developed, or may no longer work.
\ - This plugin may be safe to uninstall +deprecationWarning=This plugin is deprecated. In general, this means that it is either obsolete, no longer being developed, or may no longer work. Learn more. securityWarning=\ Warning: The currently installed plugin version may not be safe to use. Please review the following security notices: adoptThisPlugin=\ diff --git a/core/src/main/resources/hudson/PluginManager/table.jelly b/core/src/main/resources/hudson/PluginManager/table.jelly index aa63130e4e..de6927d9d2 100644 --- a/core/src/main/resources/hudson/PluginManager/table.jelly +++ b/core/src/main/resources/hudson/PluginManager/table.jelly @@ -160,6 +160,11 @@ THE SOFTWARE.
+ +
+ ${%deprecationWarning(p.getDeprecation().url)} +
+
${%adoptThisPlugin} diff --git a/core/src/main/resources/hudson/PluginManager/table.properties b/core/src/main/resources/hudson/PluginManager/table.properties index d9fe7d043f..402c8d8970 100644 --- a/core/src/main/resources/hudson/PluginManager/table.properties +++ b/core/src/main/resources/hudson/PluginManager/table.properties @@ -54,3 +54,4 @@ adoptThisPlugin=\ loading=Plugin list is loading... instructions=Use the search field above to search for available plugins. +deprecationWarning=This plugin is deprecated. In general, this means that it is either obsolete, no longer being developed, or may no longer work. Learn more.