Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[JENKINS-21486] Simplify dependency errors model and serve consistent
messages between console and administrative monitor
  • Loading branch information
Vlatombe committed Jul 29, 2016
1 parent aeb6ad9 commit b8f26b3
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 71 deletions.
122 changes: 58 additions & 64 deletions core/src/main/java/hudson/PluginWrapper.java
Expand Up @@ -34,37 +34,41 @@
import hudson.model.UpdateCenter;
import hudson.model.UpdateSite;
import hudson.util.VersionNumber;
import org.jvnet.localizer.ResourceBundleHolder;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.LogFactory;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Closeable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;

import static java.util.logging.Level.WARNING;
import static org.apache.commons.io.FilenameUtils.getBaseName;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.LogFactory;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;

import java.util.Enumeration;
import java.util.jar.JarFile;
import java.util.logging.Level;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

/**
* Represents a Jenkins plug-in and associated control information
Expand Down Expand Up @@ -150,6 +154,12 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
private final List<Dependency> dependencies;
private final List<Dependency> optionalDependencies;

public List<String> getDependencyErrors() {
return Collections.unmodifiableList(dependencyErrors);
}

private final transient List<String> dependencyErrors = new ArrayList<>();

/**
* Is this plugin bundled in jenkins.war?
*/
Expand Down Expand Up @@ -552,27 +562,28 @@ public boolean hasLicensesXml() {
if (requiredCoreVersion == null) {
LOGGER.warning(shortName + " doesn't declare required core version.");
} else {
checkRequiredCoreVersion(requiredCoreVersion);
VersionNumber actualVersion = Jenkins.getVersion();
if (actualVersion.isOlderThan(new VersionNumber(requiredCoreVersion))) {
dependencyErrors.add(Messages.PluginWrapper_obsoleteCore(Jenkins.getVersion().toString(), requiredCoreVersion));
}
}
}
List<Dependency> missingDependencies = new ArrayList<>();
List<Dependency> obsoleteDependencies = new ArrayList<>();
List<Dependency> disabledDependencies = new ArrayList<>();
// make sure dependencies exist
for (Dependency d : dependencies) {
PluginWrapper dependency = parent.getPlugin(d.shortName);
if (dependency == null) {
missingDependencies.add(d);
NOTICE.addErrorMessage(Messages.PluginWrapper_admonitor_MissingDependency(getLongName(), d.shortName));
dependencyErrors.add(Messages.PluginWrapper_missing(d.shortName, d.version));
} else {
if (dependency.isActive()) {
if (isDependencyObsolete(d, dependency)) {
obsoleteDependencies.add(d);
NOTICE.addErrorMessage(Messages.PluginWrapper_admonitor_ObsoleteDependency(getLongName(), dependency.getLongName(), d.version));
dependencyErrors.add(Messages.PluginWrapper_obsolete(dependency.getLongName(), dependency.getVersion(), d.version));
}
} else {
disabledDependencies.add(d);
NOTICE.addErrorMessage(Messages.PluginWrapper_admonitor_DisabledDependency(getLongName(), dependency.getLongName()));
if (isDependencyObsolete(d, dependency)) {
dependencyErrors.add(Messages.PluginWrapper_disabledAndObsolete(dependency.getLongName(), dependency.getVersion(), d.version));
} else {
dependencyErrors.add(Messages.PluginWrapper_disabled(dependency.getLongName()));
}
}

}
Expand All @@ -582,45 +593,24 @@ public boolean hasLicensesXml() {
PluginWrapper dependency = parent.getPlugin(d.shortName);
if (dependency != null && dependency.isActive()) {
if (isDependencyObsolete(d, dependency)) {
obsoleteDependencies.add(d);
NOTICE.addErrorMessage(Messages.PluginWrapper_admonitor_ObsoleteDependency(getLongName(), dependency.getLongName(), d.version));
dependencyErrors.add(Messages.PluginWrapper_obsolete(dependency.getLongName(), 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);
}
}

private void checkRequiredCoreVersion(String requiredCoreVersion) throws IOException {
if (Jenkins.getVersion().isOlderThan(new VersionNumber(requiredCoreVersion))) {
NOTICE.addErrorMessage(Messages.PluginWrapper_admonitor_OutdatedCoreVersion(getLongName(), requiredCoreVersion));
throw new IOException(shortName + " requires a more recent core version (" + requiredCoreVersion + ") than the current (" + Jenkins.getVersion() + ").");
if (!dependencyErrors.isEmpty()) {
NOTICE.addPlugin(this);
StringBuilder messageBuilder = new StringBuilder();
messageBuilder.append("Failed to load ").append(getLongName()).append(System.lineSeparator());
for (Iterator<String> iterator = dependencyErrors.iterator(); iterator.hasNext(); ) {
String dependencyError = iterator.next();
messageBuilder.append(" - ").append(dependencyError);
if (iterator.hasNext()) {
messageBuilder.append(System.lineSeparator());
}
}
throw new IOException(messageBuilder.toString());
}
}

Expand Down Expand Up @@ -740,14 +730,18 @@ public boolean isPinningForcingOldVersion() {
* Administrative Monitor for failed plugins
*/
public static final class PluginWrapperAdministrativeMonitor extends AdministrativeMonitor {
public final List<String> pluginError = new ArrayList<>();
private final Set<PluginWrapper> plugins = new HashSet<>();

void addErrorMessage(String error) {
pluginError.add(error);
void addPlugin(PluginWrapper plugin) {
plugins.add(plugin);
}

public boolean isActivated() {
return !pluginError.isEmpty();
return !plugins.isEmpty();
}

public Collection<PluginWrapper> getPlugins() {
return plugins;
}

/**
Expand Down
9 changes: 5 additions & 4 deletions core/src/main/resources/hudson/Messages.properties
Expand Up @@ -73,8 +73,9 @@ ProxyConfiguration.Success=Success

Functions.NoExceptionDetails=No Exception details

PluginWrapper.admonitor.OutdatedCoreVersion=Plugin {0} requires Jenkins {1} or later
PluginWrapper.admonitor.MissingDependency=Plugin {0} requires the missing plugin {1}
PluginWrapper.admonitor.DisabledDependency=Plugin {0} depends on the disabled {1}
PluginWrapper.admonitor.ObsoleteDependency=Plugin {0} requires {1} {2} or later
PluginWrapper.missing=Plugin "{0}" ({1}) is missing. To fix, install version {1} or later.
PluginWrapper.disabledAndObsolete=Plugin "{0}" ({1}) is disabled and older than required. To fix, install version {2} or later and enable it.
PluginWrapper.disabled=Plugin "{0}" is disabled. To fix, enable it.
PluginWrapper.obsolete=Plugin "{0}" ({1}) is older than required. To fix, install version {2} or later.
PluginWrapper.obsoleteCore=You must update Jenkins from {0} to version {1} or later to run this plugin.
TcpSlaveAgentListener.PingAgentProtocol.displayName=Ping protocol
Expand Up @@ -6,10 +6,17 @@
<f:submit name="correct" value="${%Correct}"/>
</div>
</form>
There are dependency errors loading some plugins:
<ul>
<j:forEach items="${it.pluginError}" var="pluginError">
<li>${pluginError}</li>
<j:forEach items="${it.plugins}" var="plugin">
<li>${plugin.longName}
<ul>
<j:forEach items="${plugin.dependencyErrors}" var="d">
<li>${d}</li>
</j:forEach>
</ul>
</li>
</j:forEach>
</ul>
</div>
</j:jelly>
</j:jelly>

0 comments on commit b8f26b3

Please sign in to comment.