diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index df64fc700f..1b3f1e7e5c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -27,7 +27,7 @@ N/A ### Submitter checklist -- [ ] JIRA issue is well described +- [ ] (If applicable) Jira issue is well described - [ ] Changelog entries and upgrade guidelines are appropriate for the audience affected by the change (users or developer, depending on the change). [Examples](https://github.com/jenkins-infra/jenkins.io/blob/master/content/_data/changelogs/weekly.yml) * Fill-in the `Proposed changelog entries` section only if there are breaking changes or other changes which may require extra steps from users during the upgrade - [ ] Appropriate autotests or explanation to why this change has no tests @@ -52,5 +52,4 @@ Before the changes are marked as `ready-for-merge`: - [ ] Changelog entries in the PR title and/or `Proposed changelog entries` are correct - [ ] Proper changelog labels are set so that the changelog can be generated automatically - [ ] If the change needs additional upgrade steps from users, `upgrade-guide-needed` label is set and there is a `Proposed upgrade guidelines` section in the PR title. ([example](https://github.com/jenkinsci/jenkins/pull/4387)) -- [ ] If it would make sense to backport the change to LTS, a JIRA issue should exist and be labeled as `lts-candidate` - +- [ ] If it would make sense to backport the change to LTS, a Jira issue must exist, be a _Bug_ or _Improvement_, and be labeled as `lts-candidate` to be considered (see [query](https://issues.jenkins-ci.org/issues/?filter=12146)). diff --git a/.gitignore b/.gitignore index 5c8b1b7ac8..2e01e8556b 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ push-build.sh war/node_modules/ war/yarn-error.log .java-version +.checkstyle diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f362e4cfb9..460f9385c6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -107,8 +107,9 @@ It is a good practice is to create branches instead of pushing to master. 4. Fill in the Pull Request description according to the [proposed template]. 5. Click _Create Pull Request_ 6. Wait for CI results/reviews, process the feedback. - * If you do not get feedback after 3 days, feel free to ping `@jenkinsci/code-reviewers` to CC. - * Usually we merge pull requests after 2 votes from reviewers or after 1 vote and 1-week delay without extra reviews. + * If you do not get feedback after 3 days, feel free to ping `@jenkinsci/core-pr-reviewers` in the comments. + * Usually we merge pull requests after 2 approvals from reviewers, no requested changes, and having waited some more time to give others an opportunity to provide more feedback. + See [this page](/docs/MAINTAINERS.adoc) for more information about our review process Once your Pull Request is ready to be merged, the repository maintainers will integrate it, prepare changelogs and diff --git a/README.md b/README.md index ee4ed648cb..5c0b818dcf 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ # About -[![Weekly Release](https://img.shields.io/badge/dynamic/json.svg?url=https://updates.jenkins.io/update-center.actual.json&label=Weekly%20Release&query=$.core.version&color=green)](https://jenkins.io/changelog/) -[![LTS Release](https://img.shields.io/badge/dynamic/json.svg?url=https://updates.jenkins.io/stable/update-center.actual.json&label=LTS%20Release&query=$.core.version&color=orange)](https://jenkins.io/changelog-stable/) [![Docker Pulls](https://img.shields.io/docker/pulls/jenkins/jenkins.svg)](https://hub.docker.com/r/jenkins/jenkins/) In a nutshell, Jenkins is the leading open-source automation server. @@ -28,7 +26,7 @@ Non-source downloads such as WAR files and several Linux packages can be found o Our latest and greatest source of Jenkins can be found on [GitHub]. Fork us! # Contributing to Jenkins -Follow the [contributing](CONTRIBUTING.md) file. +Follow the [contributing guidelines](CONTRIBUTING.md) if you want to propose a change in the Jenkins core. # News and Website All information about Jenkins can be found on our [website]. Follow us on Twitter [@jenkinsci]. @@ -43,4 +41,3 @@ Jenkins is **licensed** under the **[MIT License]**. [GitHub]: https://github.com/jenkinsci/jenkins [website]: https://jenkins.io/ [@jenkinsci]: https://twitter.com/jenkinsci -[wiki]: https://wiki.jenkins-ci.org diff --git a/bom/pom.xml b/bom/pom.xml index 6b93b53647..df6185f879 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -59,7 +59,7 @@ THE SOFTWARE. org.apache.ant ant - 1.9.14 + 1.10.7 commons-io @@ -162,7 +162,7 @@ THE SOFTWARE. commons-codec commons-codec - 1.12 + 1.14 diff --git a/core/pom.xml b/core/pom.xml index 4594bbbfd5..c6fcff4b84 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -39,9 +39,10 @@ THE SOFTWARE. true - 1.258 + 1.259 2.5.6.SEC03 2.4.12 + 2.2 @@ -75,12 +76,12 @@ THE SOFTWARE. org.jenkins-ci version-number - 1.6 + 1.7 org.jenkins-ci crypto-util - 1.1 + 1.5 org.jvnet.hudson @@ -189,10 +190,16 @@ THE SOFTWARE. jenkins-stapler-support 1.1 + + org.hamcrest + hamcrest + ${hamcrest.version} + test + org.hamcrest hamcrest-library - 1.3 + ${hamcrest.version} test @@ -314,20 +321,9 @@ THE SOFTWARE. 1.19 - javax.mail - mail - 1.4.4 - - - javax.activation - activation - - - - - org.jvnet.hudson - activation - 1.1.1-hudson-1 + com.sun.mail + jakarta.mail + 1.6.5 jaxen @@ -600,6 +596,11 @@ THE SOFTWARE. spotbugs-annotations true + + net.jcip + jcip-annotations + true + commons-fileupload @@ -890,5 +891,16 @@ THE SOFTWARE. true + + all-tests + + + !test + + + + true + + diff --git a/core/src/main/java/hudson/AboutJenkins.java b/core/src/main/java/hudson/AboutJenkins.java index 34fc120e57..9876f5701b 100644 --- a/core/src/main/java/hudson/AboutJenkins.java +++ b/core/src/main/java/hudson/AboutJenkins.java @@ -1,8 +1,11 @@ package hudson; import hudson.model.ManagementLink; +import hudson.security.Permission; import java.net.URL; +import edu.umd.cs.findbugs.annotations.NonNull; +import jenkins.model.Jenkins; import org.jenkinsci.Symbol; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -38,4 +41,15 @@ public class AboutJenkins extends ManagementLink { return AboutJenkins.class.getResource("/META-INF/licenses.xml"); } + @NonNull + @Override + public Permission getRequiredPermission() { + return Jenkins.READ; + } + + @NonNull + @Override + public Category getCategory() { + return Category.STATUS; + } } diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index e3af9fc07c..0169f79eb0 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -54,7 +54,7 @@ import org.apache.tools.zip.ZipExtraField; import org.apache.tools.zip.ZipOutputStream; import org.jenkinsci.bytecode.Transformer; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; @@ -273,7 +273,7 @@ public class ClassicPluginStrategy implements PluginStrategy { * @see DetachedPluginsUtil#getImpliedDependencies(String, String) */ @Deprecated // since TODO - @Nonnull + @NonNull public static List getImpliedDependencies(String pluginName, String jenkinsVersion) { return DetachedPluginsUtil.getImpliedDependencies(pluginName, jenkinsVersion); } diff --git a/core/src/main/java/hudson/DescriptorExtensionList.java b/core/src/main/java/hudson/DescriptorExtensionList.java index da2a0fc5f6..ec0b76337c 100644 --- a/core/src/main/java/hudson/DescriptorExtensionList.java +++ b/core/src/main/java/hudson/DescriptorExtensionList.java @@ -45,8 +45,10 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import java.util.concurrent.CopyOnWriteArrayList; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.sf.json.JSONException; import org.kohsuke.stapler.Stapler; import net.sf.json.JSONObject; @@ -132,10 +134,14 @@ public class DescriptorExtensionList, D extends Descrip } /** - * Creates a new instance of a {@link Describable} - * from the structured form submission data posted - * by a radio button group. + * Creates a new instance of a {@link Describable} from the structured form submission data posted by a radio button group. + * @param config Submitted configuration for Radio List + * @return New instance. + * {@code null} if none was selected in the radio list or if the value is filtered by a {@link hudson.model.DescriptorVisibilityFilter} + * @throws FormException Data submission error + * @since 1.312 */ + @CheckForNull public T newInstanceFromRadioList(JSONObject config) throws FormException { if(config.isNullObject()) return null; // none was selected @@ -143,8 +149,21 @@ public class DescriptorExtensionList, D extends Descrip return get(idx).newInstance(Stapler.getCurrentRequest(),config); } - public T newInstanceFromRadioList(JSONObject parent, String name) throws FormException { - return newInstanceFromRadioList(parent.getJSONObject(name)); + /** + * Creates a new instance of a {@link Describable} from the structured form submission data posted by a radio list. + * @since 1.312 + * @param name Name of the form field + * @return Created instance. + * {@code null} if none was selected in the radio list or if the value is filtered by a {@link hudson.model.DescriptorVisibilityFilter} + * @throws FormException Data submission error + */ + @CheckForNull + public T newInstanceFromRadioList(@NonNull JSONObject parent, @NonNull String name) throws FormException { + try { + return newInstanceFromRadioList(parent.getJSONObject(name)); + } catch (JSONException ex) { + throw new FormException(ex, name); + } } /** diff --git a/core/src/main/java/hudson/EnvVars.java b/core/src/main/java/hudson/EnvVars.java index e076b1e370..d3945a561a 100644 --- a/core/src/main/java/hudson/EnvVars.java +++ b/core/src/main/java/hudson/EnvVars.java @@ -43,8 +43,8 @@ import java.util.Arrays; import java.util.TreeSet; import java.util.UUID; import java.util.logging.Logger; -import javax.annotation.Nonnull; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Environment variables. @@ -101,14 +101,14 @@ public class EnvVars extends TreeMap { * @since 2.144 * @param platform the platform to set. */ - public void setPlatform(@Nonnull Platform platform) { + public void setPlatform(@NonNull Platform platform) { this.platform = platform; } public EnvVars() { super(CaseInsensitiveComparator.INSTANCE); } - public EnvVars(@Nonnull Map m) { + public EnvVars(@NonNull Map m) { this(); putAll(m); @@ -121,7 +121,7 @@ public class EnvVars extends TreeMap { } @SuppressWarnings("CopyConstructorMissesField") // does not set #platform, see its Javadoc - public EnvVars(@Nonnull EnvVars m) { + public EnvVars(@NonNull EnvVars m) { // this constructor is so that in future we can get rid of the downcasting. this((Map)m); } @@ -231,15 +231,15 @@ public class EnvVars extends TreeMap { private final Comparator comparator; - @Nonnull + @NonNull private final EnvVars target; - @Nonnull + @NonNull private final Map overrides; private Map> refereeSetMap; private List orderedVariableNames; - public OverrideOrderCalculator(@Nonnull EnvVars target, @Nonnull Map overrides) { + public OverrideOrderCalculator(@NonNull EnvVars target, @NonNull Map overrides) { comparator = target.comparator(); this.target = target; this.overrides = overrides; @@ -348,7 +348,7 @@ public class EnvVars extends TreeMap { * See {@link #override(String, String)}. * @return {@code this} */ - public EnvVars overrideExpandingAll(@Nonnull Map all) { + public EnvVars overrideExpandingAll(@NonNull Map all) { for (String key : new OverrideOrderCalculator(this, all).getOrderedVariableNames()) { override(key, expand(all.get(key))); } @@ -391,7 +391,7 @@ public class EnvVars extends TreeMap { /** * Add entire map but filter null values out. - * @since TODO + * @since 2.214 */ public void putAllNonNull(Map map) { map.forEach(this::putIfNotNull); diff --git a/core/src/main/java/hudson/ExtensionComponent.java b/core/src/main/java/hudson/ExtensionComponent.java index d274e11204..e4e35ebe1d 100644 --- a/core/src/main/java/hudson/ExtensionComponent.java +++ b/core/src/main/java/hudson/ExtensionComponent.java @@ -91,14 +91,40 @@ public class ExtensionComponent implements Comparable> if (Double.compare(a, b) > 0) return -1; if (Double.compare(a, b) < 0) return 1; - // make the order bit more deterministic among extensions of the same ordinal - if (this.instance instanceof Descriptor && that.instance instanceof Descriptor) { + boolean thisIsDescriptor = false; + String thisLabel = this.instance.getClass().getName(); + if (this.instance instanceof Descriptor) { try { - return Util.fixNull(((Descriptor)this.instance).getDisplayName()).compareTo(Util.fixNull(((Descriptor)that.instance).getDisplayName())); + thisLabel = Util.fixNull(((Descriptor) this.instance).getDisplayName()); + thisIsDescriptor = true; } catch (RuntimeException | LinkageError x) { - LOG.log(Level.WARNING, null, x); + LOG.log(Level.WARNING, "Failure during Descriptor#getDisplayName for " + this.instance.getClass().getName(), x); } } - return this.instance.getClass().getName().compareTo(that.instance.getClass().getName()); + + boolean thatIsDescriptor = false; + String thatLabel = that.instance.getClass().getName(); + if (that.instance instanceof Descriptor) { + try { + thatLabel = Util.fixNull(((Descriptor) that.instance).getDisplayName()); + thatIsDescriptor = true; + } catch (RuntimeException | LinkageError x) { + LOG.log(Level.WARNING, "Failure during Descriptor#getDisplayName for " + that.instance.getClass().getName(), x); + } + } + + if (thisIsDescriptor) { + if (thatIsDescriptor) { + return thisLabel.compareTo(thatLabel); + } else { + return 1; + } + } else { + if (thatIsDescriptor) { + return -1; + } + } + + return thisLabel.compareTo(thatLabel); } } diff --git a/core/src/main/java/hudson/ExtensionFinder.java b/core/src/main/java/hudson/ExtensionFinder.java index bd8fc132d0..ed4e3581b2 100644 --- a/core/src/main/java/hudson/ExtensionFinder.java +++ b/core/src/main/java/hudson/ExtensionFinder.java @@ -444,12 +444,7 @@ public abstract class ExtensionFinder implements ExtensionPoint { } } void error(Key key, Throwable x) { - if (verbose) { - LOGGER.log(Level.WARNING, "Failed to instantiate " + key + "; skipping this component", x); - } else { - LOGGER.log(Level.INFO, "Failed to instantiate optional component {0}; skipping", key.getTypeLiteral()); - LOGGER.log(Level.FINE, key.toString(), x); - } + LOGGER.log(verbose ? Level.WARNING : Level.FINE, "Failed to instantiate " + key + "; skipping this component", x); } }; } diff --git a/core/src/main/java/hudson/ExtensionList.java b/core/src/main/java/hudson/ExtensionList.java index c3f408c477..5be5b80bc2 100644 --- a/core/src/main/java/hudson/ExtensionList.java +++ b/core/src/main/java/hudson/ExtensionList.java @@ -45,8 +45,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.util.io.OnMaster; /** @@ -137,7 +137,7 @@ public class ExtensionList extends AbstractList implements OnMaster { * Add a listener to the extension list. * @param listener The listener. */ - public void addListener(@Nonnull ExtensionListListener listener) { + public void addListener(@NonNull ExtensionListListener listener) { listeners.add(listener); } @@ -145,7 +145,7 @@ public class ExtensionList extends AbstractList implements OnMaster { * Looks for the extension instance of the given type (subclasses excluded), * or return null. */ - public @CheckForNull U get(@Nonnull Class type) { + public @CheckForNull U get(@NonNull Class type) { for (T ext : this) if(ext.getClass()==type) return type.cast(ext); @@ -158,7 +158,7 @@ public class ExtensionList extends AbstractList implements OnMaster { * * Meant to simplify call inside @Extension annotated class to retrieve their own instance. */ - public @Nonnull U getInstance(@Nonnull Class type) throws IllegalStateException { + public @NonNull U getInstance(@NonNull Class type) throws IllegalStateException { for (T ext : this) if(ext.getClass()==type) return type.cast(ext); @@ -167,7 +167,7 @@ public class ExtensionList extends AbstractList implements OnMaster { } @Override - public @Nonnull Iterator iterator() { + public @NonNull Iterator iterator() { // we need to intercept mutation, so for now don't allow Iterator.remove return new AdaptedIterator,T>(Iterators.readOnly(ensureLoaded().iterator())) { protected T adapt(ExtensionComponent item) { @@ -309,7 +309,7 @@ public class ExtensionList extends AbstractList implements OnMaster { private List> ensureLoaded() { if(extensions!=null) return extensions; // already loaded - if (jenkins.getInitLevel().compareTo(InitMilestone.PLUGINS_PREPARED)<0) + if (jenkins == null || jenkins.getInitLevel().compareTo(InitMilestone.PLUGINS_PREPARED) < 0) return legacyInstances; // can't perform the auto discovery until all plugins are loaded, so just make the legacy instances visible synchronized (getLoadLock()) { @@ -429,7 +429,7 @@ public class ExtensionList extends AbstractList implements OnMaster { * @return some list * @since 1.572 */ - public static @Nonnull ExtensionList lookup(Class type) { + public static @NonNull ExtensionList lookup(Class type) { Jenkins j = Jenkins.getInstanceOrNull(); return j == null ? create((Jenkins) null, type) : j.getExtensionList(type); } @@ -445,7 +445,7 @@ public class ExtensionList extends AbstractList implements OnMaster { * * @since 2.87 */ - public static @Nonnull U lookupSingleton(Class type) { + public static @NonNull U lookupSingleton(Class type) { ExtensionList all = lookup(type); if (all.size() != 1) { throw new IllegalStateException("Expected 1 instance of " + type.getName() + " but got " + all.size()); diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 6b5cf97304..5b764b83ec 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -107,9 +107,9 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import jenkins.FilePathFilter; import jenkins.MasterToSlaveFileCallable; import jenkins.SlaveToMasterFileCallable; @@ -256,7 +256,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * To create a path that represents a remote path, pass in a {@link Channel} * that's connected to that machine. If {@code null}, that means the local file path. */ - public FilePath(@CheckForNull VirtualChannel channel, @Nonnull String remote) { + public FilePath(@CheckForNull VirtualChannel channel, @NonNull String remote) { this.channel = channel instanceof LocalChannel ? null : channel; this.remote = normalize(remote); } @@ -268,7 +268,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * A "local" path means a file path on the computer where the * constructor invocation happened. */ - public FilePath(@Nonnull File localPath) { + public FilePath(@NonNull File localPath) { this.channel = null; this.remote = normalize(localPath.getPath()); } @@ -278,7 +278,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * @param base starting point for resolution, and defines channel * @param rel a path which if relative will be resolved against base */ - public FilePath(@Nonnull FilePath base, @Nonnull String rel) { + public FilePath(@NonNull FilePath base, @NonNull String rel) { this.channel = base.channel; this.remote = normalize(resolvePathIfRelative(base, rel)); } @@ -288,7 +288,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { return this; } - private String resolvePathIfRelative(@Nonnull FilePath base, @Nonnull String rel) { + private String resolvePathIfRelative(@NonNull FilePath base, @NonNull String rel) { if(isAbsolute(rel)) return rel; if(base.isUnix()) { // shouldn't need this replace, but better safe than sorry @@ -303,7 +303,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { /** * Is the given path name an absolute path? */ - private static boolean isAbsolute(@Nonnull String rel) { + private static boolean isAbsolute(@NonNull String rel) { return rel.startsWith("/") || DRIVE_PATTERN.matcher(rel).matches() || UNC_PATTERN.matcher(rel).matches(); } @@ -316,7 +316,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * so remove them. */ @Restricted(NoExternalUse.class) - public static String normalize(@Nonnull String path) { + public static String normalize(@NonNull String path) { StringBuilder buf = new StringBuilder(); // Check for prefix designating absolute path Matcher m = ABSOLUTE_PREFIX_PATTERN.matcher(path); @@ -843,14 +843,14 @@ public final class FilePath implements SerializableOnlyOverRemoting { * was considered up to date. * @since 1.299 */ - public boolean installIfNecessaryFrom(@Nonnull URL archive, @CheckForNull TaskListener listener, @Nonnull String message) throws IOException, InterruptedException { + public boolean installIfNecessaryFrom(@NonNull URL archive, @CheckForNull TaskListener listener, @NonNull String message) throws IOException, InterruptedException { if (listener == null) { listener = TaskListener.NULL; } return installIfNecessaryFrom(archive, listener, message, MAX_REDIRECTS); } - private boolean installIfNecessaryFrom(@Nonnull URL archive, @Nonnull TaskListener listener, @Nonnull String message, int maxRedirects) throws InterruptedException, IOException { + private boolean installIfNecessaryFrom(@NonNull URL archive, @NonNull TaskListener listener, @NonNull String message, int maxRedirects) throws InterruptedException, IOException { try { FilePath timestamp = this.child(".timestamp"); long lastModified = timestamp.lastModified(); @@ -1321,10 +1321,13 @@ public final class FilePath implements SerializableOnlyOverRemoting { } /** - * Short for {@code getParent().child(rel)}. Useful for getting other files in the same directory. + * Short for {@code getParent().child(rel)}. Useful for getting other files in the same directory. + * @return null if {@link #getParent} would have */ + @CheckForNull public FilePath sibling(String rel) { - return getParent().child(rel); + FilePath parent = getParent(); + return parent != null ? parent.child(rel) : null; } /** @@ -1339,7 +1342,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * @param relOrAbsolute a relative or absolute path * @return a file on the same channel */ - public @Nonnull FilePath child(String relOrAbsolute) { + public @NonNull FilePath child(String relOrAbsolute) { return new FilePath(this,relOrAbsolute); } @@ -1347,6 +1350,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * Gets the parent file. * @return parent FilePath or null if there is no parent */ + @CheckForNull public FilePath getParent() { int i = remote.length() - 2; for (; i >= 0; i--) { @@ -1789,7 +1793,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { *

* This method returns direct children of the directory denoted by the 'this' object. */ - @Nonnull + @NonNull public List list() throws IOException, InterruptedException { return list((FileFilter)null); } @@ -1799,7 +1803,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * * @return can be empty but never null. Doesn't contain "." and ".." */ - @Nonnull + @NonNull public List listDirectories() throws IOException, InterruptedException { return list(new DirectoryFilter()); } @@ -1820,7 +1824,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * If this {@link FilePath} represents a remote path, * the filter object will be executed on the remote machine. */ - @Nonnull + @NonNull public List list(final FileFilter filter) throws IOException, InterruptedException { if (filter != null && !(filter instanceof Serializable)) { throw new IllegalArgumentException("Non-serializable filter of " + filter.getClass()); @@ -1856,7 +1860,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * @return * can be empty but always non-null. */ - @Nonnull + @NonNull public FilePath[] list(final String includes) throws IOException, InterruptedException { return list(includes, null); } @@ -1871,7 +1875,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * can be empty but always non-null. * @since 1.407 */ - @Nonnull + @NonNull public FilePath[] list(final String includes, final String excludes) throws IOException, InterruptedException { return list(includes, excludes, true); } @@ -1887,7 +1891,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * can be empty but always non-null. * @since 1.465 */ - @Nonnull + @NonNull public FilePath[] list(final String includes, final String excludes, final boolean defaultExcludes) throws IOException, InterruptedException { return act(new ListGlob(includes, excludes, defaultExcludes)); } @@ -1919,7 +1923,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * @return * A set of relative file names from the base directory. */ - @Nonnull + @NonNull private static String[] glob(File dir, String includes, String excludes, boolean defaultExcludes) throws IOException { if(isAbsolute(includes)) throw new IOException("Expecting Ant GLOB pattern, but saw '"+includes+"'. See http://ant.apache.org/manual/Types/fileset.html for syntax"); @@ -2359,7 +2363,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { * @return the number of files copied * @since 2.196 */ - public int copyRecursiveTo(final DirScanner scanner, final FilePath target, final String description, @Nonnull TarCompression compression) throws IOException, InterruptedException { + public int copyRecursiveTo(final DirScanner scanner, final FilePath target, final String description, @NonNull TarCompression compression) throws IOException, InterruptedException { if(this.channel==target.channel) { // local to local copy. return act(new CopyRecursiveLocal(target, scanner)); @@ -2486,7 +2490,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { private final String description; private final TarCompression compression; - ReadToTar(Pipe pipe, String description, @Nonnull TarCompression compression) { + ReadToTar(Pipe pipe, String description, @NonNull TarCompression compression) { this.pipe = pipe; this.description = description; this.compression = compression; @@ -2504,7 +2508,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { private final DirScanner scanner; private final Pipe pipe; private final TarCompression compression; - WriteToTar(DirScanner scanner, Pipe pipe, @Nonnull TarCompression compression) { + WriteToTar(DirScanner scanner, Pipe pipe, @NonNull TarCompression compression) { this.scanner = scanner; this.pipe = pipe; this.compression = compression; @@ -2520,7 +2524,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { private final Pipe pipe; private final DirScanner scanner; private final TarCompression compression; - CopyRecursiveRemoteToLocal(Pipe pipe, DirScanner scanner, @Nonnull TarCompression compression) { + CopyRecursiveRemoteToLocal(Pipe pipe, DirScanner scanner, @NonNull TarCompression compression) { this.pipe = pipe; this.scanner = scanner; this.compression = compression; @@ -2625,7 +2629,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { } private static final class IsUnix extends MasterToSlaveCallable { - @Nonnull + @NonNull public Boolean call() throws IOException { return File.pathSeparatorChar==':'; } @@ -3161,10 +3165,10 @@ public final class FilePath implements SerializableOnlyOverRemoting { /** * Channel to the current instance. */ - @Nonnull + @NonNull public static final LocalChannel localChannel = new LocalChannel(threadPoolForRemoting); - private @Nonnull SoloFilePathFilter filterNonNull() { + private @NonNull SoloFilePathFilter filterNonNull() { return filter!=null ? filter : UNRESTRICTED; } @@ -3271,20 +3275,20 @@ public final class FilePath implements SerializableOnlyOverRemoting { * TODO un-restrict it in a weekly after the patch */ @Restricted(NoExternalUse.class) - public boolean isDescendant(@Nonnull String potentialChildRelativePath) throws IOException, InterruptedException { + public boolean isDescendant(@NonNull String potentialChildRelativePath) throws IOException, InterruptedException { return act(new IsDescendant(potentialChildRelativePath)); } - private class IsDescendant extends SecureFileCallable { + private static class IsDescendant extends SecureFileCallable { private static final long serialVersionUID = 1L; private String potentialChildRelativePath; - private IsDescendant(@Nonnull String potentialChildRelativePath){ + private IsDescendant(@NonNull String potentialChildRelativePath){ this.potentialChildRelativePath = potentialChildRelativePath; } @Override - public Boolean invoke(@Nonnull File parentFile, @Nonnull VirtualChannel channel) throws IOException, InterruptedException { + public Boolean invoke(@NonNull File parentFile, @NonNull VirtualChannel channel) throws IOException, InterruptedException { if (new File(potentialChildRelativePath).isAbsolute()) { throw new IllegalArgumentException("Only a relative path is supported, the given path is absolute: " + potentialChildRelativePath); } @@ -3359,7 +3363,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { return current; } - private @Nonnull Path windowsToRealPath(@Nonnull Path path) throws IOException { + private @NonNull Path windowsToRealPath(@NonNull Path path) throws IOException { try { return path.toRealPath(); } diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index dfbaaf79ef..25d485bf1d 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -27,7 +27,9 @@ package hudson; import hudson.model.Slave; import hudson.security.*; -import jenkins.telemetry.impl.AutoRefresh; + +import java.text.SimpleDateFormat; +import java.util.function.Predicate; import jenkins.util.SystemProperties; import hudson.cli.CLICommand; import hudson.console.ConsoleAnnotationDescriptor; @@ -130,7 +132,7 @@ import java.util.logging.Logger; import java.util.logging.SimpleFormatter; import java.util.regex.Pattern; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.Nullable; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -142,6 +144,7 @@ import jenkins.model.Jenkins; import jenkins.model.ModelObjectWithChildren; import jenkins.model.ModelObjectWithContextMenu; +import org.acegisecurity.AccessDeniedException; import org.apache.commons.jelly.JellyContext; import org.apache.commons.jelly.JellyTagException; import org.apache.commons.jelly.Script; @@ -157,16 +160,14 @@ import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.jelly.InternationalizedStringExpression.RawHtmlArgument; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; import hudson.model.PasswordParameterDefinition; import hudson.util.RunList; import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.apache.commons.io.IOUtils; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -186,6 +187,9 @@ public class Functions { private static final AtomicLong iota = new AtomicLong(); private static Logger LOGGER = Logger.getLogger(Functions.class.getName()); + @Restricted(NoExternalUse.class) + public static /* non-final */ boolean UI_REFRESH = SystemProperties.getBoolean("jenkins.ui.refresh"); + public Functions() { } @@ -217,10 +221,36 @@ public class Functions { return Util.XS_DATETIME_FORMATTER.format(cal.getTime()); } + @Restricted(NoExternalUse.class) + public static String iso8601DateTime(Date date) { + return Util.XS_DATETIME_FORMATTER.format(date); + } + + /** + * Returns a localized string for the specified date, not including time. + * @param date + * @return + */ + @Restricted(NoExternalUse.class) + public static String localDate(Date date) { + return SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT).format(date); + } + public static String rfc822Date(Calendar cal) { return Util.RFC822_DATETIME_FORMATTER.format(cal.getTime()); } + /** + * Returns a human-readable string describing the time difference between now and the specified date. + * + * @param date + * @return + */ + @Restricted(NoExternalUse.class) + public static String getTimeSpanString(Date date) { + return Util.getTimeSpanString(Math.abs(date.getTime() - new Date().getTime())); + } + /** * During Jenkins start-up, before {@link InitMilestone#PLUGINS_STARTED} the extensions lists will be empty * and they are not guaranteed to be fully populated until after {@link InitMilestone#EXTENSIONS_AUGMENTED}, @@ -406,7 +436,7 @@ public class Functions { * * *

- * The head portion is the part of the URL from the {@link jenkins.model.Jenkins} + * The head portion is the part of the URL from the {@link Jenkins} * object to the first {@link Run} subtype. When "next/prev build" * is chosen, this part remains intact. * @@ -486,13 +516,23 @@ public class Functions { /** * Gets the system property indicated by the specified key. * - * Delegates to {@link SystemProperties#getString(java.lang.String)}. + * Delegates to {@link SystemProperties#getString(String)}. */ @Restricted(DoNotUse.class) public static String getSystemProperty(String key) { return SystemProperties.getString(key); } + /** + * Returns true if and only if the UI refresh is enabled. + * + * @since 2.222 + */ + @Restricted(DoNotUse.class) + public static boolean isUiRefreshEnabled() { + return UI_REFRESH; + } + public static Map getEnvVars() { return new TreeMap<>(EnvVars.masterEnvVars); } @@ -633,54 +673,17 @@ public class Functions { private static final SimpleFormatter formatter = new SimpleFormatter(); /** - * Used by {@code layout.jelly} to control the auto refresh behavior. + * No longer used. * - * @param noAutoRefresh - * On certain pages, like a page with forms, will have annoying interference - * with auto refresh. On those pages, disable auto-refresh. + * @deprecated auto refresh has been removed */ + @Deprecated public static void configureAutoRefresh(HttpServletRequest request, HttpServletResponse response, boolean noAutoRefresh) { - if(noAutoRefresh) - return; - - String param = request.getParameter("auto_refresh"); - boolean refresh = isAutoRefresh(request); - if (param != null) { - refresh = Boolean.parseBoolean(param); - Cookie c = new Cookie("hudson_auto_refresh", Boolean.toString(refresh)); - // Need to set path or it will not stick from e.g. a project page to the dashboard. - // Using request.getContextPath() might work but it seems simpler to just use the hudson_ prefix - // to avoid conflicts with any other web apps that might be on the same machine. - c.setPath("/"); - c.setMaxAge(60*60*24*30); // persist it roughly for a month - c.setHttpOnly(true); - response.addCookie(c); - } - if (refresh) { - response.addHeader("Refresh", SystemProperties.getString("hudson.Functions.autoRefreshSeconds", "10")); - } - - try { - ExtensionList.lookupSingleton(AutoRefresh.class).recordRequest(request, refresh); - } catch (Exception e) { - LOGGER.log(Level.WARNING, "Failed to record auto refresh status in telemetry", e); - } + /* feature has been removed */ } + @Deprecated public static boolean isAutoRefresh(HttpServletRequest request) { - String param = request.getParameter("auto_refresh"); - if (param != null) { - return Boolean.parseBoolean(param); - } - Cookie[] cookies = request.getCookies(); - if(cookies==null) - return false; // when API design messes it up, we all suffer - - for (Cookie c : cookies) { - if (c.getName().equals("hudson_auto_refresh")) { - return Boolean.parseBoolean(c.getValue()); - } - } return false; } @@ -1051,10 +1054,13 @@ public class Functions { * we needed this for {@link GlobalConfiguration}s are for backward compatibility. * * @param predicate - * Filter the descriptors based on {@link GlobalConfigurationCategory} + * Filter the descriptors based on this predicate * @since 1.494 + * @deprecated use {@link #getSortedDescriptorsForGlobalConfigByDescriptor(Predicate)} */ - public static Collection getSortedDescriptorsForGlobalConfig(Predicate predicate) { + @Deprecated + @Restricted(NoExternalUse.class) + public static Collection getSortedDescriptorsForGlobalConfig(com.google.common.base.Predicate predicate) { ExtensionList exts = ExtensionList.lookup(Descriptor.class); List r = new ArrayList<>(exts.size()); @@ -1079,10 +1085,47 @@ public class Functions { } /** - * Like {@link #getSortedDescriptorsForGlobalConfig(Predicate)} but with a constant truth predicate, to include all descriptors. + * Gets all the descriptors sorted by their inheritance tree of {@link Describable} + * so that descriptors of similar types come nearby. + * + *

+ * We sort them by {@link Extension#ordinal()} but only for {@link GlobalConfiguration}s, + * as the value is normally used to compare similar kinds of extensions, and we needed + * {@link GlobalConfiguration}s to be able to position themselves in a layer above. + * This however creates some asymmetry between regular {@link Descriptor}s and {@link GlobalConfiguration}s. + * Perhaps it is better to introduce another annotation element? But then, + * extensions shouldn't normally concern themselves about ordering too much, and the only reason + * we needed this for {@link GlobalConfiguration}s are for backward compatibility. + * + * @param predicate + * Filter the descriptors based on this predicate + * @since 2.222 */ - public static Collection getSortedDescriptorsForGlobalConfig() { - return getSortedDescriptorsForGlobalConfig(Predicates.alwaysTrue()); + public static Collection getSortedDescriptorsForGlobalConfigByDescriptor(Predicate predicate) { + ExtensionList exts = ExtensionList.lookup(Descriptor.class); + List r = new ArrayList<>(exts.size()); + + for (ExtensionComponent c : exts.getComponents()) { + Descriptor d = c.getInstance(); + if (d.getGlobalConfigPage()==null) continue; + + if (predicate.test(d)) { + r.add(new Tag(c.ordinal(), d)); + } + } + Collections.sort(r); + + List answer = new ArrayList<>(r.size()); + for (Tag d : r) answer.add(d.d); + + return DescriptorVisibilityFilter.apply(Jenkins.get(),answer); + } + + /** + * Like {@link #getSortedDescriptorsForGlobalConfigByDescriptor(Predicate)} but with a constant truth predicate, to include all descriptors. + */ + public static Collection getSortedDescriptorsForGlobalConfigByDescriptor() { + return getSortedDescriptorsForGlobalConfigByDescriptor(descriptor -> true); } /** @@ -1090,21 +1133,70 @@ public class Functions { */ @Deprecated public static Collection getSortedDescriptorsForGlobalConfigNoSecurity() { - return getSortedDescriptorsForGlobalConfig(Predicates.not(GlobalSecurityConfiguration.FILTER)); + return getSortedDescriptorsForGlobalConfigByDescriptor(d -> GlobalSecurityConfiguration.FILTER.negate().test(d)); } /** - * Like {@link #getSortedDescriptorsForGlobalConfig(Predicate)} but for unclassified descriptors only. + * Descriptors in the global configuration form that users with {@link Jenkins#MANAGE} permission can configure. + * * @since 1.506 */ public static Collection getSortedDescriptorsForGlobalConfigUnclassified() { - return getSortedDescriptorsForGlobalConfig(new Predicate() { - public boolean apply(GlobalConfigurationCategory cat) { - return cat instanceof GlobalConfigurationCategory.Unclassified; - } - }); + return getSortedDescriptorsForGlobalConfigByDescriptor(d -> d.getCategory() instanceof GlobalConfigurationCategory.Unclassified && Jenkins.get().hasPermission(d.getRequiredGlobalConfigPagePermission())); } - + + /** + * Descriptors shown in the global configuration form to users with {@link Jenkins#SYSTEM_READ} permission. + * + * @since 2.222 + */ + @Restricted(NoExternalUse.class) + public static Collection getSortedDescriptorsForGlobalConfigUnclassifiedReadable() { + return getSortedDescriptorsForGlobalConfigByDescriptor(d -> d.getCategory() instanceof GlobalConfigurationCategory.Unclassified && ( + Jenkins.get().hasPermission(d.getRequiredGlobalConfigPagePermission()) || Jenkins.get().hasPermission(Jenkins.SYSTEM_READ))); + } + + /** + * Checks if the current security principal has one of the supplied permissions. + * + * @throws AccessDeniedException + * if the user doesn't have the permission. + * + * @since 2.222 + */ + public static void checkAnyPermission(AccessControlled ac, Permission[] permissions) { + if (permissions == null || permissions.length == 0) { + return; + } + + ac.checkAnyPermission(permissions); + } + + /** + * This version is so that the 'checkAnyPermission' on {@code layout.jelly} + * degrades gracefully if "it" is not an {@link AccessControlled} object. + * Otherwise it will perform no check and that problem is hard to notice. + */ + public static void checkAnyPermission(Object object, Permission[] permissions) throws IOException, ServletException { + if (permissions == null || permissions.length == 0) { + return; + } + + if (object instanceof AccessControlled) + checkAnyPermission((AccessControlled) object, permissions); + else { + List ancs = Stapler.getCurrentRequest().getAncestors(); + for(Ancestor anc : Iterators.reverse(ancs)) { + Object o = anc.getObject(); + if (o instanceof AccessControlled) { + checkAnyPermission((AccessControlled) o, permissions); + return; + } + } + checkAnyPermission(Jenkins.get(), permissions); + } + } + private static class Tag implements Comparable { double ordinal; String hierarchy; @@ -1536,7 +1628,7 @@ public class Functions { * otherwise, the method returns a default * "No exception details" string. */ - public static @Nonnull String printThrowable(@CheckForNull Throwable t) { + public static @NonNull String printThrowable(@CheckForNull Throwable t) { if (t == null) { return Messages.Functions_NoExceptionDetails(); } @@ -1544,7 +1636,7 @@ public class Functions { doPrintStackTrace(s, t, null, "", new HashSet<>()); return s.toString(); } - private static void doPrintStackTrace(@Nonnull StringBuilder s, @Nonnull Throwable t, @CheckForNull Throwable higher, @Nonnull String prefix, @Nonnull Set encountered) { + private static void doPrintStackTrace(@NonNull StringBuilder s, @NonNull Throwable t, @CheckForNull Throwable higher, @NonNull String prefix, @NonNull Set encountered) { if (!encountered.add(t)) { s.append("\n"); return; @@ -1597,7 +1689,7 @@ public class Functions { * @param pw the log * @since 2.43 */ - public static void printStackTrace(@CheckForNull Throwable t, @Nonnull PrintWriter pw) { + public static void printStackTrace(@CheckForNull Throwable t, @NonNull PrintWriter pw) { pw.println(printThrowable(t).trim()); } @@ -1607,7 +1699,7 @@ public class Functions { * @param ps the log * @since 2.43 */ - public static void printStackTrace(@CheckForNull Throwable t, @Nonnull PrintStream ps) { + public static void printStackTrace(@CheckForNull Throwable t, @NonNull PrintStream ps) { ps.println(printThrowable(t).trim()); } diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index d2007e14b1..850ead6bec 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -43,7 +43,7 @@ import org.apache.commons.io.input.NullInputStream; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import java.io.BufferedOutputStream; import java.io.File; import java.io.IOException; @@ -60,7 +60,7 @@ import java.util.logging.Logger; import static org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM; import hudson.Proc.ProcWithJenkins23271Patch; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Starts a process. @@ -84,13 +84,13 @@ import javax.annotation.Nonnull; */ public abstract class Launcher { - @Nonnull + @NonNull protected final TaskListener listener; @CheckForNull protected final VirtualChannel channel; - public Launcher(@Nonnull TaskListener listener, @CheckForNull VirtualChannel channel) { + public Launcher(@NonNull TaskListener listener, @CheckForNull VirtualChannel channel) { this.listener = listener; this.channel = channel; } @@ -99,7 +99,7 @@ public abstract class Launcher { * Constructor for a decorator. * @param launcher Launcher to be decorated */ - protected Launcher(@Nonnull Launcher launcher) { + protected Launcher(@NonNull Launcher launcher) { this(launcher.listener, launcher.channel); } @@ -122,7 +122,7 @@ public abstract class Launcher { * * @return Task listener */ - @Nonnull + @NonNull public TaskListener getListener() { return listener; } @@ -265,11 +265,11 @@ public abstract class Launcher { return this; } - public ProcStarter pwd(@Nonnull File workDir) { + public ProcStarter pwd(@NonNull File workDir) { return pwd(new FilePath(workDir)); } - public ProcStarter pwd(@Nonnull String workDir) { + public ProcStarter pwd(@NonNull String workDir) { return pwd(new File(workDir)); } @@ -297,7 +297,7 @@ public abstract class Launcher { * @param out Task listener (must be safely remotable) * @return {@code this} */ - public ProcStarter stdout(@Nonnull TaskListener out) { + public ProcStarter stdout(@NonNull TaskListener out) { stdout = out.getLogger(); stdoutListener = out; return this; @@ -338,7 +338,7 @@ public abstract class Launcher { * * @return {@code this} */ - @Nonnull + @NonNull public ProcStarter stdin(@CheckForNull InputStream in) { this.stdin = in; return this; @@ -365,7 +365,7 @@ public abstract class Launcher { * @param overrides Environment variables to be overridden * @return {@code this} */ - public ProcStarter envs(@Nonnull Map overrides) { + public ProcStarter envs(@NonNull Map overrides) { this.envs = Util.mapToEnv(overrides); return this; } @@ -394,7 +394,7 @@ public abstract class Launcher { * * @return If initialized, returns a copy of internal envs array. Otherwise - a new empty array. */ - @Nonnull + @NonNull public String[] envs() { return envs != null ? envs.clone() : MemoryReductionUtil.EMPTY_STRING_ARRAY; } @@ -492,7 +492,7 @@ public abstract class Launcher { /** * Copies a {@link ProcStarter}. */ - @Nonnull + @NonNull public ProcStarter copy() { ProcStarter rhs = new ProcStarter().cmds(commands).pwd(pwd).masks(masks).stdin(stdin).stdout(stdout).stderr(stderr).envs(envs).quiet(quiet); rhs.stdoutListener = stdoutListener; @@ -507,7 +507,7 @@ public abstract class Launcher { * Launches a process by using a {@linkplain ProcStarter builder-pattern} to configure * the parameters. */ - @Nonnull + @NonNull public final ProcStarter launch() { return new ProcStarter(); } @@ -699,7 +699,7 @@ public abstract class Launcher { /** * Primarily invoked from {@link ProcStarter#start()} to start a process with a specific launcher. */ - public abstract Proc launch(@Nonnull ProcStarter starter) throws IOException; + public abstract Proc launch(@NonNull ProcStarter starter) throws IOException; /** * Launches a specified process and connects its input/output to a {@link Channel}, then @@ -720,8 +720,8 @@ public abstract class Launcher { * is inherited (if this is going to be launched from an agent, that * becomes the "current" process), these variables will be also set. */ - public abstract Channel launchChannel(@Nonnull String[] cmd, @Nonnull OutputStream out, - @CheckForNull FilePath workDir, @Nonnull Map envVars) throws IOException, InterruptedException; + public abstract Channel launchChannel(@NonNull String[] cmd, @NonNull OutputStream out, + @CheckForNull FilePath workDir, @NonNull Map envVars) throws IOException, InterruptedException; /** * Returns true if this {@link Launcher} is going to launch on Unix. @@ -738,7 +738,7 @@ public abstract class Launcher { /** * Prints out the command line to the listener so that users know what we are doing. */ - protected final void printCommandLine(@Nonnull String[] cmd, @CheckForNull FilePath workDir) { + protected final void printCommandLine(@NonNull String[] cmd, @CheckForNull FilePath workDir) { StringBuilder buf = new StringBuilder(); if (workDir != null) { buf.append('['); @@ -772,7 +772,7 @@ public abstract class Launcher { * remain unmasked ({@code false}). * @param workDir The work dir. */ - protected final void maskedPrintCommandLine(@Nonnull List cmd, @CheckForNull boolean[] mask, @CheckForNull FilePath workDir) { + protected final void maskedPrintCommandLine(@NonNull List cmd, @CheckForNull boolean[] mask, @CheckForNull FilePath workDir) { if(mask==null) { printCommandLine(cmd.toArray(new String[0]),workDir); return; @@ -790,7 +790,7 @@ public abstract class Launcher { printCommandLine(masked, workDir); } - protected final void maskedPrintCommandLine(@Nonnull String[] cmd, @Nonnull boolean[] mask, @CheckForNull FilePath workDir) { + protected final void maskedPrintCommandLine(@NonNull String[] cmd, @NonNull boolean[] mask, @CheckForNull FilePath workDir) { maskedPrintCommandLine(Arrays.asList(cmd),mask,workDir); } @@ -800,8 +800,8 @@ public abstract class Launcher { * @param node Node for which this launcher is created. * @return Decorated instance of the Launcher. */ - @Nonnull - public final Launcher decorateFor(@Nonnull Node node) { + @NonNull + public final Launcher decorateFor(@NonNull Node node) { Launcher l = this; for (LauncherDecorator d : LauncherDecorator.all()) l = d.decorate(l,node); @@ -815,7 +815,7 @@ public abstract class Launcher { * @param prefix Prefixes to be appended * @since 1.299 */ - @Nonnull + @NonNull public final Launcher decorateByPrefix(final String... prefix) { final Launcher outer = this; return new Launcher(outer) { @@ -844,14 +844,14 @@ public abstract class Launcher { outer.kill(modelEnvVars); } - private String[] prefix(@Nonnull String[] args) { + private String[] prefix(@NonNull String[] args) { String[] newArgs = new String[args.length+prefix.length]; System.arraycopy(prefix,0,newArgs,0,prefix.length); System.arraycopy(args,0,newArgs,prefix.length,args.length); return newArgs; } - private boolean[] prefix(@Nonnull boolean[] args) { + private boolean[] prefix(@NonNull boolean[] args) { boolean[] newArgs = new boolean[args.length+prefix.length]; System.arraycopy(args,0,newArgs,prefix.length,args.length); return newArgs; @@ -868,8 +868,8 @@ public abstract class Launcher { * * @since 1.489 */ - @Nonnull - public final Launcher decorateByEnv(@Nonnull EnvVars _env) { + @NonNull + public final Launcher decorateByEnv(@NonNull EnvVars _env) { final EnvVars env = new EnvVars(_env); final Launcher outer = this; return new Launcher(outer) { @@ -908,7 +908,7 @@ public abstract class Launcher { * {@link Launcher} that launches process locally. */ public static class LocalLauncher extends Launcher { - public LocalLauncher(@Nonnull TaskListener listener) { + public LocalLauncher(@NonNull TaskListener listener) { this(listener, FilePath.localChannel); } @@ -1003,7 +1003,7 @@ public abstract class Launcher { @Restricted(NoExternalUse.class) public static class DummyLauncher extends Launcher { - public DummyLauncher(@Nonnull TaskListener listener) { + public DummyLauncher(@NonNull TaskListener listener) { super(listener, null); } @@ -1030,13 +1030,13 @@ public abstract class Launcher { public static class RemoteLauncher extends Launcher { private final boolean isUnix; - public RemoteLauncher(@Nonnull TaskListener listener, @Nonnull VirtualChannel channel, boolean isUnix) { + public RemoteLauncher(@NonNull TaskListener listener, @NonNull VirtualChannel channel, boolean isUnix) { super(listener, channel); this.isUnix = isUnix; } @Override - @Nonnull + @NonNull public VirtualChannel getChannel() { VirtualChannel vc = super.getChannel(); if (vc == null) { @@ -1179,7 +1179,7 @@ public abstract class Launcher { private final Launcher inner; - public DecoratedLauncher(@Nonnull Launcher inner) { + public DecoratedLauncher(@NonNull Launcher inner) { super(inner); this.inner = inner; } @@ -1241,7 +1241,7 @@ public abstract class Launcher { * Gets nested launcher. * @return Inner launcher */ - @Nonnull + @NonNull public Launcher getInner() { return inner; } @@ -1262,28 +1262,28 @@ public abstract class Launcher { void kill() throws IOException, InterruptedException; boolean isAlive() throws IOException, InterruptedException; - @Nonnull + @NonNull IOTriplet getIOtriplet(); } private static class RemoteLaunchCallable extends MasterToSlaveCallable { - private final @Nonnull List cmd; + private final @NonNull List cmd; private final @CheckForNull boolean[] masks; private final @CheckForNull String[] env; private final @CheckForNull InputStream in; private final @CheckForNull OutputStream out; private final @CheckForNull OutputStream err; private final @CheckForNull String workDir; - private final @Nonnull TaskListener listener; + private final @NonNull TaskListener listener; private final @CheckForNull TaskListener stdoutListener; private final boolean reverseStdin, reverseStdout, reverseStderr; private final boolean quiet; - RemoteLaunchCallable(@Nonnull List cmd, @CheckForNull boolean[] masks, @CheckForNull String[] env, + RemoteLaunchCallable(@NonNull List cmd, @CheckForNull boolean[] masks, @CheckForNull String[] env, @CheckForNull InputStream in, boolean reverseStdin, @CheckForNull OutputStream out, boolean reverseStdout, @CheckForNull OutputStream err, boolean reverseStderr, - boolean quiet, @CheckForNull String workDir, @Nonnull TaskListener listener, @CheckForNull TaskListener stdoutListener) { + boolean quiet, @CheckForNull String workDir, @NonNull TaskListener listener, @CheckForNull TaskListener stdoutListener) { this.cmd = new ArrayList<>(cmd); this.masks = masks; this.env = env; @@ -1355,19 +1355,19 @@ public abstract class Launcher { } private static class RemoteChannelLaunchCallable extends MasterToSlaveCallable { - @Nonnull + @NonNull private final String[] cmd; - @Nonnull + @NonNull private final Pipe out; @CheckForNull private final String workDir; - @Nonnull + @NonNull private final OutputStream err; - @Nonnull + @NonNull private final Map envOverrides; - public RemoteChannelLaunchCallable(@Nonnull String[] cmd, @Nonnull Pipe out, @Nonnull OutputStream err, - @CheckForNull String workDir, @Nonnull Map envOverrides) { + public RemoteChannelLaunchCallable(@NonNull String[] cmd, @NonNull Pipe out, @NonNull OutputStream err, + @CheckForNull String workDir, @NonNull Map envOverrides) { this.cmd = cmd; this.out = out; this.err = new RemoteOutputStream(err); @@ -1413,7 +1413,7 @@ public abstract class Launcher { /** * Expands the list of environment variables by inheriting current env variables. */ - private static EnvVars inherit(@Nonnull Map overrides) { + private static EnvVars inherit(@NonNull Map overrides) { EnvVars m = new EnvVars(EnvVars.masterEnvVars); m.overrideExpandingAll(overrides); return m; diff --git a/core/src/main/java/hudson/LauncherDecorator.java b/core/src/main/java/hudson/LauncherDecorator.java index b73beb4e21..372f8200b9 100644 --- a/core/src/main/java/hudson/LauncherDecorator.java +++ b/core/src/main/java/hudson/LauncherDecorator.java @@ -6,7 +6,7 @@ import hudson.model.Node; import hudson.model.Executor; import hudson.model.TaskListener; import hudson.tasks.BuildWrapper; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Decorates {@link Launcher} so that one can intercept executions of commands @@ -42,8 +42,8 @@ public abstract class LauncherDecorator implements ExtensionPoint { * @see Launcher#decorateFor(Node) * @see Launcher#decorateByPrefix(String[]) */ - @Nonnull - public abstract Launcher decorate(@Nonnull Launcher launcher, @Nonnull Node node); + @NonNull + public abstract Launcher decorate(@NonNull Launcher launcher, @NonNull Node node); /** * Returns all the registered {@link LauncherDecorator}s. diff --git a/core/src/main/java/hudson/LocalPluginManager.java b/core/src/main/java/hudson/LocalPluginManager.java index fbc4cd5d4c..4bfccf9e40 100644 --- a/core/src/main/java/hudson/LocalPluginManager.java +++ b/core/src/main/java/hudson/LocalPluginManager.java @@ -27,8 +27,8 @@ package hudson; import jenkins.util.SystemProperties; import jenkins.model.Jenkins; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletContext; import java.io.File; import java.util.Collection; @@ -46,7 +46,7 @@ public class LocalPluginManager extends PluginManager { * @param context Servlet context. Provided for compatibility as {@code Jenkins.get().servletContext} should be used. * @param rootDir Jenkins home directory. */ - public LocalPluginManager(@CheckForNull ServletContext context, @Nonnull File rootDir) { + public LocalPluginManager(@CheckForNull ServletContext context, @NonNull File rootDir) { super(context, new File(rootDir,"plugins")); } @@ -54,7 +54,7 @@ public class LocalPluginManager extends PluginManager { * Creates a new LocalPluginManager * @param jenkins Jenkins instance that will use the plugin manager. */ - public LocalPluginManager(@Nonnull Jenkins jenkins) { + public LocalPluginManager(@NonNull Jenkins jenkins) { this(jenkins.servletContext, jenkins.getRootDir()); } @@ -62,7 +62,7 @@ public class LocalPluginManager extends PluginManager { * Creates a new LocalPluginManager * @param rootDir Jenkins home directory. */ - public LocalPluginManager(@Nonnull File rootDir) { + public LocalPluginManager(@NonNull File rootDir) { this(null, rootDir); } diff --git a/core/src/main/java/hudson/Main.java b/core/src/main/java/hudson/Main.java index b4490d4188..96db6cd7ae 100644 --- a/core/src/main/java/hudson/Main.java +++ b/core/src/main/java/hudson/Main.java @@ -220,7 +220,8 @@ public class Main { public static boolean isUnitTest = false; /** - * Set to true if we are running inside {@code mvn hpi:run} or {@code mvn jetty:run}. + * Set to true if we are running inside {@code mvn jetty:run}. + * This is also set if running inside {@code mvn hpi:run} since plugins parent POM 2.30. */ public static boolean isDevelopmentMode = SystemProperties.getBoolean(Main.class.getName()+".development"); diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index d4ec6a3dd2..58a419b568 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -105,8 +105,8 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.xml.parsers.ParserConfigurationException; @@ -126,6 +126,7 @@ import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; @@ -134,6 +135,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; @@ -209,29 +211,29 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas private enum PMConstructor { JENKINS { @Override - @Nonnull - PluginManager doCreate(@Nonnull Class klass, - @Nonnull Jenkins jenkins) throws ReflectiveOperationException { + @NonNull + PluginManager doCreate(@NonNull Class klass, + @NonNull Jenkins jenkins) throws ReflectiveOperationException { return klass.getConstructor(Jenkins.class).newInstance(jenkins); } }, SC_FILE { @Override - @Nonnull PluginManager doCreate(@Nonnull Class klass, - @Nonnull Jenkins jenkins) throws ReflectiveOperationException { + @NonNull PluginManager doCreate(@NonNull Class klass, + @NonNull Jenkins jenkins) throws ReflectiveOperationException { return klass.getConstructor(ServletContext.class, File.class).newInstance(jenkins.servletContext, jenkins.getRootDir()); } }, FILE { @Override - @Nonnull PluginManager doCreate(@Nonnull Class klass, - @Nonnull Jenkins jenkins) throws ReflectiveOperationException { + @NonNull PluginManager doCreate(@NonNull Class klass, + @NonNull Jenkins jenkins) throws ReflectiveOperationException { return klass.getConstructor(File.class).newInstance(jenkins.getRootDir()); } }; - final @CheckForNull PluginManager create(@Nonnull Class klass, - @Nonnull Jenkins jenkins) throws ReflectiveOperationException { + final @CheckForNull PluginManager create(@NonNull Class klass, + @NonNull Jenkins jenkins) throws ReflectiveOperationException { try { return doCreate(klass, jenkins); } catch(NoSuchMethodException e) { @@ -240,8 +242,8 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } } - abstract @Nonnull PluginManager doCreate(@Nonnull Class klass, - @Nonnull Jenkins jenkins) throws ReflectiveOperationException; + abstract @NonNull PluginManager doCreate(@NonNull Class klass, + @NonNull Jenkins jenkins) throws ReflectiveOperationException; } /** @@ -251,7 +253,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas * @return Plugin manager to use. If no custom class is configured or in case of any error, the default * {@link LocalPluginManager} is returned. */ - public static @Nonnull PluginManager createDefault(@Nonnull Jenkins jenkins) { + public static @NonNull PluginManager createDefault(@NonNull Jenkins jenkins) { String pmClassName = SystemProperties.getString(CUSTOM_PLUGIN_MANAGER); if (!StringUtils.isBlank(pmClassName)) { LOGGER.log(FINE, String.format("Use of custom plugin manager [%s] requested.", pmClassName)); @@ -371,7 +373,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } public Api getApi() { - Jenkins.get().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(Jenkins.SYSTEM_READ); return new Api(this); } @@ -624,13 +626,13 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } } - protected @Nonnull Set loadPluginsFromWar(@Nonnull String fromPath) { + protected @NonNull Set loadPluginsFromWar(@NonNull String fromPath) { return loadPluginsFromWar(fromPath, null); } //TODO: Consider refactoring in order to avoid DMI_COLLECTION_OF_URLS @SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS", justification = "Plugin loading happens only once on Jenkins startup") - protected @Nonnull Set loadPluginsFromWar(@Nonnull String fromPath, @CheckForNull FilenameFilter filter) { + protected @NonNull Set loadPluginsFromWar(@NonNull String fromPath, @CheckForNull FilenameFilter filter) { Set names = new HashSet(); ServletContext context = Jenkins.get().servletContext; @@ -819,12 +821,12 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } } - private String normalisePluginName(@Nonnull String name) { + private String normalisePluginName(@NonNull String name) { // Normalise the name by stripping off the file extension (if present)... return name.replace(".jpi", "").replace(".hpi", ""); } - private @CheckForNull VersionNumber getPluginVersion(@Nonnull File dir, @Nonnull String pluginId) { + private @CheckForNull VersionNumber getPluginVersion(@NonNull File dir, @NonNull String pluginId) { VersionNumber version = getPluginVersion(new File(dir, pluginId + ".jpi")); if (version == null) { version = getPluginVersion(new File(dir, pluginId + ".hpi")); @@ -832,7 +834,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas return version; } - private @CheckForNull VersionNumber getPluginVersion(@Nonnull File pluginFile) { + private @CheckForNull VersionNumber getPluginVersion(@NonNull File pluginFile) { if (!pluginFile.exists()) { return null; } @@ -843,7 +845,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } } - private @CheckForNull VersionNumber getPluginVersion(@Nonnull URL pluginURL) { + private @CheckForNull VersionNumber getPluginVersion(@NonNull URL pluginURL) { Manifest manifest = parsePluginManifest(pluginURL); if (manifest == null) { return null; @@ -1102,8 +1104,8 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas * @return Input stream, which allows to retrieve manifest. This stream must be closed outside * @throws IOException Operation error */ - @Nonnull - /*package*/ static InputStream getBundledJpiManifestStream(@Nonnull URL url) throws IOException { + @NonNull + /*package*/ static InputStream getBundledJpiManifestStream(@NonNull URL url) throws IOException { URLConnection uc = url.openConnection(); InputStream in = null; // Magic, which allows to avoid using stream generated for JarURLConnection. @@ -1142,8 +1144,8 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas * @return Modification date * @throws IOException Operation error */ - @Nonnull - /*package*/ static long getModificationDate(@Nonnull URL url) throws IOException { + @NonNull + /*package*/ static long getModificationDate(@NonNull URL url) throws IOException { URLConnection uc = url.openConnection(); // It prevents file descriptor leak if the URL references a file within JAR @@ -1394,7 +1396,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas @RequirePOST public HttpResponse doUpdateSources(StaplerRequest req) throws IOException { - Jenkins.get().checkPermission(CONFIGURE_UPDATECENTER); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); if (req.hasParameter("remove")) { UpdateCenter uc = Jenkins.get().getUpdateCenter(); @@ -1492,11 +1494,11 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas * @since 2.0 */ @Restricted(NoExternalUse.class) - public List> install(@Nonnull Collection plugins, boolean dynamicLoad) { + public List> install(@NonNull Collection plugins, boolean dynamicLoad) { return install(plugins, dynamicLoad, null); } - private List> install(@Nonnull Collection plugins, boolean dynamicLoad, @CheckForNull UUID correlationId) { + private List> install(@NonNull Collection plugins, boolean dynamicLoad, @CheckForNull UUID correlationId) { List> installJobs = new ArrayList<>(); LOGGER.log(INFO, "Starting installation of a batch of {0} plugins plus their dependencies", plugins.size()); @@ -1600,7 +1602,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas @RequirePOST public HttpResponse doSiteConfigure(@QueryParameter String site) throws IOException { Jenkins hudson = Jenkins.get(); - hudson.checkPermission(CONFIGURE_UPDATECENTER); + hudson.checkPermission(Jenkins.ADMINISTER); UpdateCenter uc = hudson.getUpdateCenter(); PersistedList sites = uc.getSites(); for (UpdateSite s : sites) { @@ -1615,7 +1617,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas @POST public HttpResponse doProxyConfigure(StaplerRequest req) throws IOException, ServletException { Jenkins jenkins = Jenkins.get(); - jenkins.checkPermission(CONFIGURE_UPDATECENTER); + jenkins.checkPermission(Jenkins.ADMINISTER); ProxyConfiguration pc = req.bindJSON(ProxyConfiguration.class, req.getSubmittedForm()); if (pc.name==null) { @@ -1634,7 +1636,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas @RequirePOST public HttpResponse doUploadPlugin(StaplerRequest req) throws IOException, ServletException { try { - Jenkins.get().checkPermission(UPLOAD_PLUGINS); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory()); @@ -1703,7 +1705,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas @Restricted(NoExternalUse.class) @RequirePOST public HttpResponse doCheckUpdatesServer() throws IOException { - Jenkins.get().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(Jenkins.SYSTEM_READ); // We'll check the update servers with a try-retry mechanism. The retrier is built with a builder Retrier updateServerRetrier = new Retrier.Builder<>( @@ -1958,7 +1960,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas * @return the list of results for every plugin and their dependent plugins. * @throws IOException see {@link PluginWrapper#disable()} */ - public @Nonnull List disablePlugins(@Nonnull PluginWrapper.PluginDisableStrategy strategy, @Nonnull List plugins) throws IOException { + public @NonNull List disablePlugins(@NonNull PluginWrapper.PluginDisableStrategy strategy, @NonNull List plugins) throws IOException { // Where we store the results of each plugin disablement List results = new ArrayList<>(plugins.size()); @@ -2105,7 +2107,12 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } public static boolean FAST_LOOKUP = !SystemProperties.getBoolean(PluginManager.class.getName()+".noFastLookup"); + /** @deprecated in Jenkins 2.222 use {@link Jenkins#ADMINISTER} instead */ + @Deprecated public static final Permission UPLOAD_PLUGINS = new Permission(Jenkins.PERMISSIONS, "UploadPlugins", Messages._PluginManager_UploadPluginsPermission_Description(),Jenkins.ADMINISTER,PermissionScope.JENKINS); + + /** @deprecated in Jenkins 2.222 use {@link Jenkins#ADMINISTER} instead */ + @Deprecated public static final Permission CONFIGURE_UPDATECENTER = new Permission(Jenkins.PERMISSIONS, "ConfigureUpdateCenter", Messages._PluginManager_ConfigureUpdateCenterPermission_Description(),Jenkins.ADMINISTER,PermissionScope.JENKINS); /** @@ -2263,15 +2270,47 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } } + @Restricted(DoNotUse.class) + public String unscientific(double d) { + return String.format(Locale.US, "%15.4f", d); + } + @Override @Restricted(NoExternalUse.class) public Object getTarget() { if (!SKIP_PERMISSION_CHECK) { - Jenkins.get().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(Jenkins.SYSTEM_READ); } return this; } + @Restricted(DoNotUse.class) // Used from table.jelly + public boolean isMetaLabel(String label) { + return "adopt-this-plugin".equals(label); + } + + @Restricted(DoNotUse.class) // Used from table.jelly + public boolean hasAdoptThisPluginLabel(UpdateSite.Plugin plugin) { + final String[] categories = plugin.categories; + if (categories == null) { + return false; + } + return Arrays.asList(categories).contains("adopt-this-plugin"); + } + + @Restricted(DoNotUse.class) // Used from table.jelly + public boolean hasAdoptThisPluginLabel(PluginWrapper plugin) { + final UpdateSite.Plugin pluginMeta = Jenkins.get().getUpdateCenter().getPlugin(plugin.getShortName()); + if (pluginMeta == null) { + return false; + } + final String[] categories = pluginMeta.categories; + if (categories == null) { + return false; + } + return Arrays.asList(categories).contains("adopt-this-plugin"); + } + /** * Escape hatch for StaplerProxy-based access control */ diff --git a/core/src/main/java/hudson/PluginManagerStaplerOverride.java b/core/src/main/java/hudson/PluginManagerStaplerOverride.java index 3103ba1818..1b076df39e 100644 --- a/core/src/main/java/hudson/PluginManagerStaplerOverride.java +++ b/core/src/main/java/hudson/PluginManagerStaplerOverride.java @@ -1,7 +1,7 @@ package hudson; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Extension point for selectively overriding parts of the {@link PluginManager} views @@ -20,7 +20,7 @@ public abstract class PluginManagerStaplerOverride implements ExtensionPoint { * Return all implementations of this extension point * @return All implementations of this extension point */ - public static @Nonnull ExtensionList all() { + public static @NonNull ExtensionList all() { return ExtensionList.lookup(PluginManagerStaplerOverride.class); } } diff --git a/core/src/main/java/hudson/PluginStrategy.java b/core/src/main/java/hudson/PluginStrategy.java index 7804b73b2b..0a4a6e7968 100644 --- a/core/src/main/java/hudson/PluginStrategy.java +++ b/core/src/main/java/hudson/PluginStrategy.java @@ -29,7 +29,7 @@ import java.io.IOException; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Pluggability point for how to create {@link PluginWrapper}. @@ -56,7 +56,7 @@ public interface PluginStrategy extends ExtensionPoint { * Needed by {@link PluginManager#dynamicLoad} to decide whether such a plugin is already installed. * @return the {@link PluginWrapper#getShortName} */ - @Nonnull String getShortName(File archive) throws IOException; + @NonNull String getShortName(File archive) throws IOException; /** * Loads the plugin and starts it. diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index 5b738e8237..4084f43488 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -36,6 +36,7 @@ import hudson.util.VersionNumber; import io.jenkins.lib.versionnumber.JavaSpecificationVersion; import jenkins.YesNoMaybe; import jenkins.model.Jenkins; +import jenkins.security.UpdateSiteWarningsMonitor; import jenkins.util.java.JavaUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.LogFactory; @@ -50,8 +51,8 @@ import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import org.kohsuke.stapler.interceptor.RequirePOST; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.Closeable; import java.io.File; import java.io.IOException; @@ -226,7 +227,7 @@ public class PluginWrapper implements Comparable, ModelObject { * Set the list of components that depend on this plugin. * @param dependents The list of components that depend on this plugin. */ - public void setDependents(@Nonnull Set dependents) { + public void setDependents(@NonNull Set dependents) { this.dependents = dependents; } @@ -234,7 +235,7 @@ public class PluginWrapper implements Comparable, ModelObject { * @deprecated Please use {@link #setDependents}. */ @Deprecated - public void setDependants(@Nonnull Set dependents) { + public void setDependants(@NonNull Set dependents) { setDependents(dependents); } @@ -242,7 +243,7 @@ public class PluginWrapper implements Comparable, ModelObject { * Set the list of components that depend optionally on this plugin. * @param optionalDependents The list of components that depend optionally on this plugin. */ - public void setOptionalDependents(@Nonnull Set optionalDependents) { + public void setOptionalDependents(@NonNull Set optionalDependents) { this.optionalDependents = optionalDependents; } @@ -250,7 +251,7 @@ public class PluginWrapper implements Comparable, ModelObject { * @deprecated Please use {@link #setOptionalDependents}. */ @Deprecated - public void setOptionalDependants(@Nonnull Set optionalDependents) { + public void setOptionalDependants(@NonNull Set optionalDependents) { setOptionalDependents(dependents); } @@ -259,7 +260,7 @@ public class PluginWrapper implements Comparable, ModelObject { * Note that the list will include elements of {@link #getOptionalDependents}. * @return The list of components that depend on this plugin. */ - public @Nonnull Set getDependents() { + public @NonNull Set getDependents() { if (isBundled && dependents.isEmpty()) { return CORE_ONLY_DEPENDANT; } else { @@ -271,7 +272,7 @@ public class PluginWrapper implements Comparable, ModelObject { * @deprecated Please use {@link #getDependents}. */ @Deprecated - public @Nonnull Set getDependants() { + public @NonNull Set getDependants() { return getDependents(); } @@ -279,7 +280,7 @@ public class PluginWrapper implements Comparable, ModelObject { * Like {@link #getDependents} but excluding optional dependencies. * @since 2.181 */ - public @Nonnull Set getMandatoryDependents() { + public @NonNull Set getMandatoryDependents() { Set s = new HashSet<>(dependents); s.removeAll(optionalDependents); return s; @@ -288,7 +289,7 @@ public class PluginWrapper implements Comparable, ModelObject { /** * @return The list of components that depend optionally on this plugin. */ - public @Nonnull Set getOptionalDependents() { + public @NonNull Set getOptionalDependents() { return optionalDependents; } @@ -296,7 +297,7 @@ public class PluginWrapper implements Comparable, ModelObject { * @deprecated Please use {@link #getOptionalDependents}. */ @Deprecated - public @Nonnull Set getOptionalDependants() { + public @NonNull Set getOptionalDependants() { return getOptionalDependents(); } @@ -448,7 +449,7 @@ public class PluginWrapper implements Comparable, ModelObject { } public Api getApi() { - Jenkins.get().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkAnyPermission(Jenkins.SYSTEM_READ, Jenkins.MANAGE); return new Api(this); } @@ -715,7 +716,7 @@ public class PluginWrapper implements Comparable, ModelObject { * @param strategy strategy to use * @return an object representing the result of the disablement of this plugin and its dependents plugins. */ - public @Nonnull PluginDisableResult disable(@Nonnull PluginDisableStrategy strategy) { + public @NonNull PluginDisableResult disable(@NonNull PluginDisableStrategy strategy) { PluginDisableResult result = new PluginDisableResult(shortName); if (!this.isEnabled()) { @@ -963,7 +964,7 @@ public class PluginWrapper implements Comparable, ModelObject { /** * Similar to {@code org.apache.maven.artifact.ArtifactUtils.isSnapshot}. */ - static boolean isSnapshot(@Nonnull String version) { + static boolean isSnapshot(@NonNull String version) { return version.contains("-SNAPSHOT") || version.matches(".+-[0-9]{8}.[0-9]{6}-[0-9]+"); } @@ -1034,6 +1035,21 @@ public class PluginWrapper implements Comparable, ModelObject { return DetachedPluginsUtil.isDetachedPlugin(shortName); } + @Restricted(NoExternalUse.class) + public boolean hasImpliedDependents() { + if (!isDetached()) { + return false; + } + for (PluginWrapper p : Jenkins.get().getPluginManager().getPlugins()) { + for (Dependency dependency : DetachedPluginsUtil.getImpliedDependencies(p.shortName, p.getRequiredCoreVersion())) { + if (dependency.shortName.equals(shortName)) { + return true; + } + } + } + return false; + } + /** * Sort by short name. */ @@ -1272,6 +1288,10 @@ public class PluginWrapper implements Comparable, ModelObject { return HttpResponses.redirectViaContextPath("/pluginManager/installed"); // send back to plugin manager } + @Restricted(DoNotUse.class) // Jelly + public List getActiveWarnings() { + return ExtensionList.lookupSingleton(UpdateSiteWarningsMonitor.class).getActivePluginWarningsByPlugin().getOrDefault(this, Collections.emptyList()); + } private static final Logger LOGGER = Logger.getLogger(PluginWrapper.class.getName()); diff --git a/core/src/main/java/hudson/Proc.java b/core/src/main/java/hudson/Proc.java index f387d6abcc..ad825bb10e 100644 --- a/core/src/main/java/hudson/Proc.java +++ b/core/src/main/java/hudson/Proc.java @@ -51,7 +51,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; diff --git a/core/src/main/java/hudson/ProxyConfiguration.java b/core/src/main/java/hudson/ProxyConfiguration.java index f79d15ef1d..c9448150ff 100644 --- a/core/src/main/java/hudson/ProxyConfiguration.java +++ b/core/src/main/java/hudson/ProxyConfiguration.java @@ -49,7 +49,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import jenkins.model.Jenkins; import jenkins.security.stapler.StaplerAccessibleType; import jenkins.util.JenkinsJVM; diff --git a/core/src/main/java/hudson/TcpSlaveAgentListener.java b/core/src/main/java/hudson/TcpSlaveAgentListener.java index 39ba388d2d..d86b534fb1 100644 --- a/core/src/main/java/hudson/TcpSlaveAgentListener.java +++ b/core/src/main/java/hudson/TcpSlaveAgentListener.java @@ -29,7 +29,7 @@ import java.io.SequenceInputStream; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.security.interfaces.RSAPublicKey; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.Nullable; import hudson.model.AperiodicWork; import hudson.util.VersionNumber; @@ -360,7 +360,7 @@ public final class TcpSlaveAgentListener extends Thread { } // This is essentially just to be able to pass the parent thread into the callback, as it can't access it otherwise - private abstract class ConnectionHandlerFailureCallback { + private static abstract class ConnectionHandlerFailureCallback { private Thread parentThread; public ConnectionHandlerFailureCallback(Thread parentThread) { diff --git a/core/src/main/java/hudson/UDPBroadcastThread.java b/core/src/main/java/hudson/UDPBroadcastThread.java index 57bbe7fc30..4c4900585b 100644 --- a/core/src/main/java/hudson/UDPBroadcastThread.java +++ b/core/src/main/java/hudson/UDPBroadcastThread.java @@ -25,12 +25,10 @@ package hudson; import hudson.init.Initializer; -import jenkins.model.Jenkins; import jenkins.util.SystemProperties; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import java.io.IOException; import java.util.logging.Logger; import static hudson.init.InitMilestone.COMPLETED; diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index 776fea2c41..a10a8864e1 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -85,9 +85,10 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckReturnValue; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -113,8 +114,8 @@ public class Util { * Creates a filtered sublist. * @since 1.176 */ - @Nonnull - public static List filter( @Nonnull Iterable base, @Nonnull Class type ) { + @NonNull + public static List filter( @NonNull Iterable base, @NonNull Class type ) { List r = new ArrayList<>(); for (Object i : base) { if(type.isInstance(i)) @@ -126,8 +127,8 @@ public class Util { /** * Creates a filtered sublist. */ - @Nonnull - public static List filter( @Nonnull List base, @Nonnull Class type ) { + @NonNull + public static List filter( @NonNull List base, @NonNull Class type ) { return filter((Iterable)base,type); } @@ -144,7 +145,7 @@ public class Util { * */ @Nullable - public static String replaceMacro( @CheckForNull String s, @Nonnull Map properties) { + public static String replaceMacro( @CheckForNull String s, @NonNull Map properties) { return replaceMacro(s, new VariableResolver.ByMap<>(properties)); } @@ -155,7 +156,7 @@ public class Util { * Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.) */ @Nullable - public static String replaceMacro(@CheckForNull String s, @Nonnull VariableResolver resolver) { + public static String replaceMacro(@CheckForNull String s, @NonNull VariableResolver resolver) { if (s == null) { return null; } @@ -196,9 +197,9 @@ public class Util { * instead to specify the charset to use for decoding (preferably * {@link java.nio.charset.StandardCharsets#UTF_8}). */ - @Nonnull + @NonNull @Deprecated - public static String loadFile(@Nonnull File logfile) throws IOException { + public static String loadFile(@NonNull File logfile) throws IOException { return loadFile(logfile, Charset.defaultCharset()); } @@ -211,8 +212,8 @@ public class Util { * @return The entire text content of {@code logfile}. * @throws IOException If an error occurs while reading the file. */ - @Nonnull - public static String loadFile(@Nonnull File logfile, @Nonnull Charset charset) throws IOException { + @NonNull + public static String loadFile(@NonNull File logfile, @NonNull Charset charset) throws IOException { // Note: Until charset handling is resolved (e.g. by implementing // https://issues.jenkins-ci.org/browse/JENKINS-48923 ), this method // must be able to handle character encoding errors. As reported at @@ -245,7 +246,7 @@ public class Util { * @throws IOException * if the operation fails. */ - public static void deleteContentsRecursive(@Nonnull File file) throws IOException { + public static void deleteContentsRecursive(@NonNull File file) throws IOException { deleteContentsRecursive(fileToPath(file), PathRemover.PathChecker.ALLOW_ALL); } @@ -256,7 +257,7 @@ public class Util { * @throws IOException if the operation fails */ @Restricted(NoExternalUse.class) - public static void deleteContentsRecursive(@Nonnull Path path, @Nonnull PathRemover.PathChecker pathChecker) throws IOException { + public static void deleteContentsRecursive(@NonNull Path path, @NonNull PathRemover.PathChecker pathChecker) throws IOException { newPathRemover(pathChecker).forceRemoveDirectoryContents(path); } @@ -267,7 +268,7 @@ public class Util { * @param f a file to delete * @throws IOException if it exists but could not be successfully deleted */ - public static void deleteFile(@Nonnull File f) throws IOException { + public static void deleteFile(@NonNull File f) throws IOException { newPathRemover(PathRemover.PathChecker.ALLOW_ALL).forceRemoveFile(fileToPath(f)); } @@ -279,7 +280,7 @@ public class Util { * @throws IOException * if the operation fails. */ - public static void deleteRecursive(@Nonnull File dir) throws IOException { + public static void deleteRecursive(@NonNull File dir) throws IOException { deleteRecursive(fileToPath(dir), PathRemover.PathChecker.ALLOW_ALL); } @@ -290,7 +291,7 @@ public class Util { * @throws IOException if the operation fails */ @Restricted(NoExternalUse.class) - public static void deleteRecursive(@Nonnull Path dir, @Nonnull PathRemover.PathChecker pathChecker) throws IOException { + public static void deleteRecursive(@NonNull Path dir, @NonNull PathRemover.PathChecker pathChecker) throws IOException { newPathRemover(pathChecker).forceRemoveRecursive(dir); } @@ -314,12 +315,12 @@ public class Util { * considers NTFS junction points as symbolic * links. */ - public static boolean isSymlink(@Nonnull File file) throws IOException { + public static boolean isSymlink(@NonNull File file) throws IOException { return isSymlink(fileToPath(file)); } @Restricted(NoExternalUse.class) - public static boolean isSymlink(@Nonnull Path path) { + public static boolean isSymlink(@NonNull Path path) { /* * Windows Directory Junctions are effectively the same as Linux symlinks to directories. * Unfortunately, the Java 7 NIO2 API function isSymbolicLink does not treat them as such. @@ -411,14 +412,14 @@ public class Util { * On Windows, error messages for IOException aren't very helpful. * This method generates additional user-friendly error message to the listener */ - public static void displayIOException(@Nonnull IOException e, @Nonnull TaskListener listener ) { + public static void displayIOException(@NonNull IOException e, @NonNull TaskListener listener ) { String msg = getWin32ErrorMessage(e); if(msg!=null) listener.getLogger().println(msg); } @CheckForNull - public static String getWin32ErrorMessage(@Nonnull IOException e) { + public static String getWin32ErrorMessage(@NonNull IOException e) { return getWin32ErrorMessage((Throwable)e); } @@ -468,7 +469,7 @@ public class Util { /** * Guesses the current host name. */ - @Nonnull + @NonNull public static String getHostName() { try { return InetAddress.getLocalHost().getHostName(); @@ -481,7 +482,7 @@ public class Util { * @deprecated Use {@link IOUtils#copy(InputStream, OutputStream)} */ @Deprecated - public static void copyStream(@Nonnull InputStream in,@Nonnull OutputStream out) throws IOException { + public static void copyStream(@NonNull InputStream in,@NonNull OutputStream out) throws IOException { IOUtils.copy(in, out); } @@ -489,7 +490,7 @@ public class Util { * @deprecated Use {@link IOUtils#copy(Reader, Writer)} */ @Deprecated - public static void copyStream(@Nonnull Reader in, @Nonnull Writer out) throws IOException { + public static void copyStream(@NonNull Reader in, @NonNull Writer out) throws IOException { IOUtils.copy(in, out); } @@ -497,7 +498,7 @@ public class Util { * @deprecated Use {@link IOUtils#copy(InputStream, OutputStream)} in a {@code try}-with-resources block */ @Deprecated - public static void copyStreamAndClose(@Nonnull InputStream in, @Nonnull OutputStream out) throws IOException { + public static void copyStreamAndClose(@NonNull InputStream in, @NonNull OutputStream out) throws IOException { try (InputStream _in = in; OutputStream _out = out) { // make sure both are closed, and use Throwable.addSuppressed IOUtils.copy(_in, _out); } @@ -507,7 +508,7 @@ public class Util { * @deprecated Use {@link IOUtils#copy(Reader, Writer)} in a {@code try}-with-resources block */ @Deprecated - public static void copyStreamAndClose(@Nonnull Reader in, @Nonnull Writer out) throws IOException { + public static void copyStreamAndClose(@NonNull Reader in, @NonNull Writer out) throws IOException { try (Reader _in = in; Writer _out = out) { IOUtils.copy(_in, _out); } @@ -523,21 +524,21 @@ public class Util { * @since 1.145 * @see QuotedStringTokenizer */ - @Nonnull - public static String[] tokenize(@Nonnull String s, @CheckForNull String delimiter) { + @NonNull + public static String[] tokenize(@NonNull String s, @CheckForNull String delimiter) { return QuotedStringTokenizer.tokenize(s,delimiter); } - @Nonnull - public static String[] tokenize(@Nonnull String s) { + @NonNull + public static String[] tokenize(@NonNull String s) { return tokenize(s," \t\n\r\f"); } /** * Converts the map format of the environment variables to the K=V format in the array. */ - @Nonnull - public static String[] mapToEnv(@Nonnull Map m) { + @NonNull + public static String[] mapToEnv(@NonNull Map m) { String[] r = new String[m.size()]; int idx=0; @@ -547,7 +548,7 @@ public class Util { return r; } - public static int min(int x, @Nonnull int... values) { + public static int min(int x, @NonNull int... values) { for (int i : values) { if(i List createSubList(@Nonnull Collection source, @Nonnull Class type ) { + @NonNull + public static List createSubList(@NonNull Collection source, @NonNull Class type ) { List r = new ArrayList<>(); for (Object item : source) { if(type.isInstance(item)) @@ -814,8 +815,8 @@ public class Util { * {@link #rawEncode(String)} should generally be used instead, though be careful to pass only * a single path component to that method (it will encode /, but this method does not). */ - @Nonnull - public static String encode(@Nonnull String s) { + @NonNull + public static String encode(@NonNull String s) { try { boolean escaped = false; @@ -872,8 +873,8 @@ public class Util { * single path component used in constructing a URL. * Method name inspired by PHP's rawurlencode. */ - @Nonnull - public static String rawEncode(@Nonnull String s) { + @NonNull + public static String rawEncode(@NonNull String s) { boolean escaped = false; StringBuilder out = null; CharsetEncoder enc = null; @@ -982,8 +983,8 @@ public class Util { return buf.toString(); } - @Nonnull - public static String xmlEscape(@Nonnull String text) { + @NonNull + public static String xmlEscape(@NonNull String text) { StringBuilder buf = new StringBuilder(text.length()+64); for( int i=0; i T fixNull(@CheckForNull T s, @Nonnull T defaultValue) { + @NonNull + public static T fixNull(@CheckForNull T s, @NonNull T defaultValue) { return s != null ? s : defaultValue; } @@ -1070,7 +1071,7 @@ public class Util { * {@code l} if l is not {@code null}. * An empty immutable list if l is {@code null}. */ - @Nonnull + @NonNull public static List fixNull(@CheckForNull List l) { return fixNull(l, Collections.emptyList()); } @@ -1084,7 +1085,7 @@ public class Util { * {@code l} if l is not {@code null}. * An empty immutable set if l is {@code null}. */ - @Nonnull + @NonNull public static Set fixNull(@CheckForNull Set l) { return fixNull(l, Collections.emptySet()); } @@ -1098,7 +1099,7 @@ public class Util { * {@code l} if l is not {@code null}. * An empty immutable set if l is {@code null}. */ - @Nonnull + @NonNull public static Collection fixNull(@CheckForNull Collection l) { return fixNull(l, Collections.emptySet()); } @@ -1112,7 +1113,7 @@ public class Util { * {@code l} if l is not {@code null}. * An empty immutable set if l is {@code null}. */ - @Nonnull + @NonNull public static Iterable fixNull(@CheckForNull Iterable l) { return fixNull(l, Collections.emptySet()); } @@ -1120,8 +1121,8 @@ public class Util { /** * Cuts all the leading path portion and get just the file name. */ - @Nonnull - public static String getFileName(@Nonnull String filePath) { + @NonNull + public static String getFileName(@NonNull String filePath) { int idx = filePath.lastIndexOf('\\'); if(idx>=0) return getFileName(filePath.substring(idx+1)); @@ -1134,8 +1135,8 @@ public class Util { /** * Concatenate multiple strings by inserting a separator. */ - @Nonnull - public static String join(@Nonnull Collection strings, @Nonnull String separator) { + @NonNull + public static String join(@NonNull Collection strings, @NonNull String separator) { StringBuilder buf = new StringBuilder(); boolean first=true; for (Object s : strings) { @@ -1149,8 +1150,8 @@ public class Util { /** * Combines all the given collections into a single list. */ - @Nonnull - public static List join(@Nonnull Collection... items) { + @NonNull + public static List join(@NonNull Collection... items) { int size = 0; for (Collection item : items) size += item.size(); @@ -1177,8 +1178,8 @@ public class Util { * Can be null. * @since 1.172 */ - @Nonnull - public static FileSet createFileSet(@Nonnull File baseDir, @Nonnull String includes, @CheckForNull String excludes) { + @NonNull + public static FileSet createFileSet(@NonNull File baseDir, @NonNull String includes, @CheckForNull String excludes) { FileSet fs = new FileSet(); fs.setDir(baseDir); fs.setProject(new Project()); @@ -1200,11 +1201,70 @@ public class Util { return fs; } - @Nonnull - public static FileSet createFileSet(@Nonnull File baseDir, @Nonnull String includes) { + @NonNull + public static FileSet createFileSet(@NonNull File baseDir, @NonNull String includes) { return createFileSet(baseDir,includes,null); } + private static void tryToDeleteSymlink(@NonNull File symlink) { + if (!symlink.delete()) { + LogRecord record = new LogRecord(Level.FINE, "Failed to delete temporary symlink {0}"); + record.setParameters(new Object[]{symlink.getAbsolutePath()}); + LOGGER.log(record); + } + } + + private static void reportAtomicFailure(@NonNull Path pathForSymlink, @NonNull Exception ex) { + LogRecord record = new LogRecord(Level.FINE, "Failed to atomically create/replace symlink {0}"); + record.setParameters(new Object[]{pathForSymlink.toAbsolutePath().toString()}); + record.setThrown(ex); + LOGGER.log(record); + } + + /** + * Creates a symlink to targetPath at baseDir+symlinkPath. + * + * @param pathForSymlink + * The absolute path of the symlink itself as a path object. + * @param fileForSymlink + * The absolute path of the symlink itself as a file object. + * @param target + * The path that the symlink should point to. Usually relative to the directory of the symlink but may instead be an absolute path. + * @param symlinkPath + * Where to create a symlink in (relative to {@code baseDir}) + * + * Returns true on success + */ + @CheckReturnValue + private static boolean createSymlinkAtomic(@NonNull Path pathForSymlink, @NonNull File fileForSymlink, @NonNull Path target, @NonNull String symlinkPath) { + try { + File symlink = File.createTempFile("symtmp", null, fileForSymlink); + tryToDeleteSymlink(symlink); + Path tempSymlinkPath = symlink.toPath(); + Files.createSymbolicLink(tempSymlinkPath, target); + try { + Files.move(tempSymlinkPath, pathForSymlink, java.nio.file.StandardCopyOption.ATOMIC_MOVE); + return true; + } catch ( + UnsupportedOperationException | + SecurityException | + IOException ex) { + // If we couldn't perform an atomic move or the setup, we fall through to another approach + reportAtomicFailure(pathForSymlink, ex); + } + // If we didn't return after our atomic move, then we want to clean up our symlink + tryToDeleteSymlink(symlink); + } catch ( + SecurityException | + InvalidPathException | + UnsupportedOperationException | + IOException ex) { + // We couldn't perform an atomic move or the setup. + reportAtomicFailure(pathForSymlink, ex); + } + return false; + } + /** * Creates a symlink to targetPath at baseDir+symlinkPath. *

@@ -1217,18 +1277,23 @@ public class Util { * @param symlinkPath * Where to create a symlink in (relative to {@code baseDir}) */ - public static void createSymlink(@Nonnull File baseDir, @Nonnull String targetPath, - @Nonnull String symlinkPath, @Nonnull TaskListener listener) throws InterruptedException { + public static void createSymlink(@NonNull File baseDir, @NonNull String targetPath, + @NonNull String symlinkPath, @NonNull TaskListener listener) throws InterruptedException { + File fileForSymlink = new File(baseDir, symlinkPath); try { - Path path = fileToPath(new File(baseDir, symlinkPath)); + Path pathForSymlink = fileToPath(fileForSymlink); Path target = Paths.get(targetPath, MemoryReductionUtil.EMPTY_STRING_ARRAY); + if (createSymlinkAtomic(pathForSymlink, fileForSymlink, target, symlinkPath)) { + return; + } + final int maxNumberOfTries = 4; final int timeInMillis = 100; for (int tryNumber = 1; tryNumber <= maxNumberOfTries; tryNumber++) { - Files.deleteIfExists(path); + Files.deleteIfExists(pathForSymlink); try { - Files.createSymbolicLink(path, target); + Files.createSymbolicLink(pathForSymlink, target); break; } catch (FileAlreadyExistsException fileAlreadyExistsException) { if (tryNumber < maxNumberOfTries) { @@ -1249,7 +1314,7 @@ public class Util { return; } PrintStream log = listener.getLogger(); - log.printf("ln %s %s failed%n",targetPath, new File(baseDir, symlinkPath)); + log.printf("ln %s %s failed%n", targetPath, fileForSymlink); Functions.printStackTrace(e, log); } } @@ -1277,7 +1342,7 @@ public class Util { * if the specified file is not a symlink. */ @CheckForNull - public static File resolveSymlinkToFile(@Nonnull File link) throws InterruptedException, IOException { + public static File resolveSymlinkToFile(@NonNull File link) throws InterruptedException, IOException { String target = resolveSymlink(link); if (target==null) return null; @@ -1298,7 +1363,7 @@ public class Util { * The relative path is meant to be resolved from the location of the symlink. */ @CheckForNull - public static String resolveSymlink(@Nonnull File link) throws IOException { + public static String resolveSymlink(@NonNull File link) throws IOException { try { Path path = fileToPath(link); return Files.readSymbolicLink(path).toString(); @@ -1340,8 +1405,8 @@ public class Util { * Wraps with the error icon and the CSS class to render error message. * @since 1.173 */ - @Nonnull - public static String wrapToErrorSpan(@Nonnull String s) { + @NonNull + public static String wrapToErrorSpan(@NonNull String s) { s = ""+s+""; return s; } @@ -1370,11 +1435,11 @@ public class Util { * Checks if the method defined on the base type with the given arguments * is overridden in the given derived type. */ - public static boolean isOverridden(@Nonnull Class base, @Nonnull Class derived, @Nonnull String methodName, @Nonnull Class... types) { + public static boolean isOverridden(@NonNull Class base, @NonNull Class derived, @NonNull String methodName, @NonNull Class... types) { return !getMethod(base, methodName, types).equals(getMethod(derived, methodName, types)); } - private static Method getMethod(@Nonnull Class clazz, @Nonnull String methodName, @Nonnull Class... types) { + private static Method getMethod(@NonNull Class clazz, @NonNull String methodName, @NonNull Class... types) { Method res = null; try { res = clazz.getDeclaredMethod(methodName, types); @@ -1405,8 +1470,8 @@ public class Util { * @param ext * For example, ".zip" */ - @Nonnull - public static File changeExtension(@Nonnull File dst, @Nonnull String ext) { + @NonNull + public static File changeExtension(@NonNull File dst, @NonNull String ext) { String p = dst.getPath(); int pos = p.lastIndexOf('.'); if (pos<0) return new File(p+ext); @@ -1434,7 +1499,7 @@ public class Util { @Deprecated @RestrictedSince("1.651.2 / 2.TODO") @Restricted(NoExternalUse.class) - public static boolean isAbsoluteUri(@Nonnull String uri) { + public static boolean isAbsoluteUri(@NonNull String uri) { int idx = uri.indexOf(':'); if (idx<0) return false; // no ':'. can't be absolute @@ -1446,7 +1511,7 @@ public class Util { * Return true iff the parameter does not denote an absolute URI and not a scheme-relative URI. * @since 2.3 / 1.651.2 */ - public static boolean isSafeToRedirectTo(@Nonnull String uri) { + public static boolean isSafeToRedirectTo(@NonNull String uri) { return !isAbsoluteUri(uri) && !uri.startsWith("//"); } @@ -1454,7 +1519,7 @@ public class Util { * Works like {@link String#indexOf(int)} but 'not found' is returned as s.length(), not -1. * This enables more straight-forward comparison. */ - private static int _indexOf(@Nonnull String s, char ch) { + private static int _indexOf(@NonNull String s, char ch) { int idx = s.indexOf(ch); if (idx<0) return s.length(); return idx; @@ -1464,8 +1529,8 @@ public class Util { * Loads a key/value pair string as {@link Properties} * @since 1.392 */ - @Nonnull - public static Properties loadProperties(@Nonnull String properties) throws IOException { + @NonNull + public static Properties loadProperties(@NonNull String properties) throws IOException { Properties p = new Properties(); p.load(new StringReader(properties)); return p; @@ -1481,8 +1546,8 @@ public class Util { * @since 2.19, but TODO update once un-restricted */ @Restricted(NoExternalUse.class) - public static void closeAndLogFailures(@CheckForNull Closeable toClose, @Nonnull Logger logger, - @Nonnull String closeableName, @Nonnull String closeableOwner) { + public static void closeAndLogFailures(@CheckForNull Closeable toClose, @NonNull Logger logger, + @NonNull String closeableName, @NonNull String closeableOwner) { if (toClose == null) { return; } @@ -1533,7 +1598,7 @@ public class Util { * @throws IOException if {@code f.toPath()} throws {@link InvalidPathException}. */ @Restricted(NoExternalUse.class) - public static @Nonnull Path fileToPath(@Nonnull File file) throws IOException { + public static @NonNull Path fileToPath(@NonNull File file) throws IOException { try { return file.toPath(); } catch (InvalidPathException e) { @@ -1547,7 +1612,7 @@ public class Util { * even if there are only 3 hours between. As well as "10am" to "2pm" both on the same day, returns 0. */ @Restricted(NoExternalUse.class) - public static long daysBetween(@Nonnull Date a, @Nonnull Date b){ + public static long daysBetween(@NonNull Date a, @NonNull Date b){ LocalDate aLocal = a.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); LocalDate bLocal = b.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); return ChronoUnit.DAYS.between(aLocal, bLocal); @@ -1558,7 +1623,7 @@ public class Util { * @see #daysBetween(Date, Date) */ @Restricted(NoExternalUse.class) - public static long daysElapsedSince(@Nonnull Date date){ + public static long daysElapsedSince(@NonNull Date date){ return Math.max(0, daysBetween(date, new Date())); } @@ -1567,7 +1632,7 @@ public class Util { * Useful for an ancestor we know is inside the URL to ease readability */ @Restricted(NoExternalUse.class) - public static @Nonnull T getNearestAncestorOfTypeOrThrow(@Nonnull StaplerRequest request, @Nonnull Class clazz) { + public static @NonNull T getNearestAncestorOfTypeOrThrow(@NonNull StaplerRequest request, @NonNull Class clazz) { T t = request.findAncestorObject(clazz); if (t == null) { throw new IllegalArgumentException("No ancestor of type " + clazz.getName() + " in the request"); @@ -1643,7 +1708,7 @@ public class Util { @Restricted(value = NoExternalUse.class) static boolean GC_AFTER_FAILED_DELETE = SystemProperties.getBoolean(Util.class.getName() + ".performGCOnFailedDelete"); - private static PathRemover newPathRemover(@Nonnull PathRemover.PathChecker pathChecker) { + private static PathRemover newPathRemover(@NonNull PathRemover.PathChecker pathChecker) { return PathRemover.newFilteredRobustRemover(pathChecker, DELETION_RETRIES, GC_AFTER_FAILED_DELETE, WAIT_BETWEEN_DELETION_RETRIES); } diff --git a/core/src/main/java/hudson/WebAppMain.java b/core/src/main/java/hudson/WebAppMain.java index 1dcadb036d..26daa5731b 100644 --- a/core/src/main/java/hudson/WebAppMain.java +++ b/core/src/main/java/hudson/WebAppMain.java @@ -48,6 +48,8 @@ import hudson.util.ChartUtil; import hudson.util.AWTProblem; import jenkins.util.JenkinsJVM; import org.jvnet.localizer.LocaleProvider; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.jelly.JellyFacet; import org.apache.tools.ant.types.FileSet; @@ -58,6 +60,7 @@ import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletResponse; +import javax.servlet.SessionTrackingMode; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import java.io.File; @@ -65,6 +68,7 @@ import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.Date; +import java.util.EnumSet; import java.util.Locale; import java.util.logging.Level; import java.util.logging.Logger; @@ -83,6 +87,28 @@ public class WebAppMain implements ServletContextListener { // use RingBufferLogHandler class name to configure for backward compatibility private static final int DEFAULT_RING_BUFFER_SIZE = SystemProperties.getInteger(RingBufferLogHandler.class.getName() + ".defaultSize", 256); + /** + * System property name to force the session tracking by cookie. + * This prevents Tomcat to use the URL tracking in addition to the cookie by default. + * This could be useful for instances that requires to have + * the {@link jenkins.security.SuspiciousRequestFilter#allowSemicolonsInPath} turned off. + *

+ * If you allow semicolon in URL and the session to be tracked by URL and you have + * a SecurityRealm that does not invalidate session after authentication, + * your instance is vulnerable to session hijacking. + *

+ * The SecurityRealm should be corrected but this is a hardening in Jenkins core. + *

+ * As this property is read during startup, you will not be able to change it at runtime + * depending on your application server (not possible with Jetty nor Tomcat) + *

+ * When running hpi:run, the default tracking is COOKIE+URL. + * When running java -jar with Winstone/Jetty, the default setting is set to COOKIE only. + * When running inside Tomcat, the default setting is COOKIE+URL. + */ + @Restricted(NoExternalUse.class) + public static final String FORCE_SESSION_TRACKING_BY_COOKIE_PROP = WebAppMain.class.getName() + ".forceSessionTrackingByCookie"; + private final RingBufferLogHandler handler = new RingBufferLogHandler(DEFAULT_RING_BUFFER_SIZE) { @Override public synchronized void publish(LogRecord record) { if (record.getLevel().intValue() >= Level.INFO.intValue()) { @@ -223,6 +249,9 @@ public class WebAppMain implements ServletContextListener { installExpressionFactory(event); context.setAttribute(APP,new HudsonIsLoading()); + if (SystemProperties.getBoolean(FORCE_SESSION_TRACKING_BY_COOKIE_PROP, true)) { + context.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE)); + } final File _home = home; initThread = new Thread("Jenkins initialization thread") { diff --git a/core/src/main/java/hudson/cli/CLIAction.java b/core/src/main/java/hudson/cli/CLIAction.java index 102dbbc00b..efca422c44 100644 --- a/core/src/main/java/hudson/cli/CLIAction.java +++ b/core/src/main/java/hudson/cli/CLIAction.java @@ -190,7 +190,7 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy { } } - class ServerSideImpl extends PlainCLIProtocol.ServerSide { + static class ServerSideImpl extends PlainCLIProtocol.ServerSide { private Thread runningThread; private boolean ready; private final List args = new ArrayList<>(); diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java index 68747a6a8c..a1f11658cd 100644 --- a/core/src/main/java/hudson/cli/CLICommand.java +++ b/core/src/main/java/hudson/cli/CLICommand.java @@ -64,8 +64,8 @@ import java.util.Locale; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Base class for Hudson CLI. @@ -234,7 +234,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { // add options from the authenticator SecurityContext sc = null; Authentication old = null; - Authentication auth = null; + Authentication auth; try { sc = SecurityContextHolder.getContext(); old = sc.getAuthentication(); @@ -251,51 +251,31 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { new Object[] {getName(), args.size(), auth.getName(), res}); return res; } catch (CmdLineException e) { - LOGGER.log(Level.FINE, String.format("Failed call to CLI command %s, with %d arguments, as user %s.", - getName(), args.size(), auth != null ? auth.getName() : ""), e); - stderr.println(); - stderr.println("ERROR: " + e.getMessage()); + logFailedCommandAndPrintExceptionErrorMessage(args, e); printUsage(stderr, p); return 2; } catch (IllegalStateException e) { - LOGGER.log(Level.FINE, String.format("Failed call to CLI command %s, with %d arguments, as user %s.", - getName(), args.size(), auth != null ? auth.getName() : ""), e); - stderr.println(); - stderr.println("ERROR: " + e.getMessage()); + logFailedCommandAndPrintExceptionErrorMessage(args, e); return 4; } catch (IllegalArgumentException e) { - LOGGER.log(Level.FINE, String.format("Failed call to CLI command %s, with %d arguments, as user %s.", - getName(), args.size(), auth != null ? auth.getName() : ""), e); - stderr.println(); - stderr.println("ERROR: " + e.getMessage()); + logFailedCommandAndPrintExceptionErrorMessage(args, e); return 3; } catch (AbortException e) { - LOGGER.log(Level.FINE, String.format("Failed call to CLI command %s, with %d arguments, as user %s.", - getName(), args.size(), auth != null ? auth.getName() : ""), e); - // signals an error without stack trace - stderr.println(); - stderr.println("ERROR: " + e.getMessage()); + logFailedCommandAndPrintExceptionErrorMessage(args, e); return 5; } catch (AccessDeniedException e) { - LOGGER.log(Level.FINE, String.format("Failed call to CLI command %s, with %d arguments, as user %s.", - getName(), args.size(), auth != null ? auth.getName() : ""), e); - stderr.println(); - stderr.println("ERROR: " + e.getMessage()); + logFailedCommandAndPrintExceptionErrorMessage(args, e); return 6; } catch (BadCredentialsException e) { // to the caller, we can't reveal whether the user didn't exist or the password didn't match. // do that to the server log instead String id = UUID.randomUUID().toString(); - LOGGER.log(Level.INFO, "CLI login attempt failed: " + id, e); - stderr.println(); - stderr.println("ERROR: Bad Credentials. Search the server log for " + id + " for more details."); + logAndPrintError(e, "Bad Credentials. Search the server log for " + id + " for more details.", + "CLI login attempt failed: " + id, Level.INFO); return 7; } catch (Throwable e) { - final String errorMsg = String.format("Unexpected exception occurred while performing %s command.", - getName()); - stderr.println(); - stderr.println("ERROR: " + errorMsg); - LOGGER.log(Level.WARNING, errorMsg, e); + String errorMsg = "Unexpected exception occurred while performing " + getName() + " command."; + logAndPrintError(e, errorMsg, errorMsg, Level.WARNING); Functions.printStackTrace(e, stderr); return 1; } finally { @@ -304,6 +284,20 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { } } + private void logFailedCommandAndPrintExceptionErrorMessage(List args, Throwable e) { + Authentication auth = getTransportAuthentication(); + String logMessage = String.format("Failed call to CLI command %s, with %d arguments, as user %s.", + getName(), args.size(), auth != null ? auth.getName() : ""); + + logAndPrintError(e, e.getMessage(), logMessage, Level.FINE); + } + + private void logAndPrintError(Throwable e, String errorMessage, String logMessage, Level logLevel) { + LOGGER.log(logLevel, logMessage, e); + this.stderr.println(); + this.stderr.println("ERROR: " + errorMessage); + } + /** * Get parser for this command. * @@ -461,11 +455,11 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { * Define the encoding for the command. * @since 2.54 */ - public void setClientCharset(@Nonnull Charset encoding) { + public void setClientCharset(@NonNull Charset encoding) { this.encoding = encoding; } - protected @Nonnull Charset getClientCharset() throws IOException, InterruptedException { + protected @NonNull Charset getClientCharset() throws IOException, InterruptedException { if (encoding != null) { return encoding; } diff --git a/core/src/main/java/hudson/cli/Connection.java b/core/src/main/java/hudson/cli/Connection.java index bdeb192fd3..13cec3f615 100644 --- a/core/src/main/java/hudson/cli/Connection.java +++ b/core/src/main/java/hudson/cli/Connection.java @@ -27,7 +27,6 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.remoting.ClassFilter; import hudson.remoting.ObjectInputStreamEx; import hudson.remoting.SocketChannelStream; -import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; @@ -56,6 +55,7 @@ import java.security.Signature; import java.security.interfaces.DSAPublicKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; import org.jenkinsci.remoting.util.AnonymousClassWarnings; /** @@ -123,11 +123,11 @@ public class Connection { } public void writeKey(Key key) throws IOException { - writeUTF(new String(Base64.encodeBase64(key.getEncoded()))); + writeUTF(Base64.getEncoder().encodeToString(key.getEncoded())); } public X509EncodedKeySpec readKey() throws IOException { - byte[] otherHalf = Base64.decodeBase64(readUTF()); // for historical reasons, we don't use readByteArray() + byte[] otherHalf = Base64.getDecoder().decode(readUTF()); // for historical reasons, we don't use readByteArray() return new X509EncodedKeySpec(otherHalf); } diff --git a/core/src/main/java/hudson/cli/CreateNodeCommand.java b/core/src/main/java/hudson/cli/CreateNodeCommand.java index 36aa7242db..791982daec 100644 --- a/core/src/main/java/hudson/cli/CreateNodeCommand.java +++ b/core/src/main/java/hudson/cli/CreateNodeCommand.java @@ -27,8 +27,6 @@ package hudson.cli; import hudson.Extension; import hudson.model.Computer; import hudson.model.Node; -import hudson.model.Slave; -import hudson.model.User; import jenkins.model.Jenkins; import org.kohsuke.args4j.Argument; diff --git a/core/src/main/java/hudson/cli/GroovyCommand.java b/core/src/main/java/hudson/cli/GroovyCommand.java index 9a802bc5a1..8279f7ba4e 100644 --- a/core/src/main/java/hudson/cli/GroovyCommand.java +++ b/core/src/main/java/hudson/cli/GroovyCommand.java @@ -59,7 +59,7 @@ public class GroovyCommand extends CLICommand { protected int run() throws Exception { // this allows the caller to manipulate the JVM state, so require the execute script privilege. - Jenkins.get().checkPermission(Jenkins.RUN_SCRIPTS); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); Binding binding = new Binding(); binding.setProperty("out",new PrintWriter(stdout,true)); diff --git a/core/src/main/java/hudson/cli/GroovyshCommand.java b/core/src/main/java/hudson/cli/GroovyshCommand.java index dd2584ebf0..c8255f6456 100644 --- a/core/src/main/java/hudson/cli/GroovyshCommand.java +++ b/core/src/main/java/hudson/cli/GroovyshCommand.java @@ -60,7 +60,7 @@ public class GroovyshCommand extends CLICommand { @Override protected int run() { // this allows the caller to manipulate the JVM state, so require the admin privilege. - Jenkins.get().checkPermission(Jenkins.RUN_SCRIPTS); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); // this being remote means no jline capability is available System.setProperty("jline.terminal", UnsupportedTerminal.class.getName()); diff --git a/core/src/main/java/hudson/cli/InstallPluginCommand.java b/core/src/main/java/hudson/cli/InstallPluginCommand.java index 09cb54c95c..7b5985c75e 100644 --- a/core/src/main/java/hudson/cli/InstallPluginCommand.java +++ b/core/src/main/java/hudson/cli/InstallPluginCommand.java @@ -83,7 +83,7 @@ public class InstallPluginCommand extends CLICommand { @Override protected int run() throws Exception { Jenkins h = Jenkins.get(); - h.checkPermission(PluginManager.UPLOAD_PLUGINS); + h.checkPermission(Jenkins.ADMINISTER); PluginManager pm = h.getPluginManager(); if (name != null) { diff --git a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java index f8363f3250..7b997eb177 100644 --- a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java +++ b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java @@ -45,7 +45,7 @@ import org.jvnet.localizer.ResourceBundleHolder; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.CmdLineException; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; @@ -97,7 +97,7 @@ public class CLIRegisterer extends ExtensionFinder { return null; } - private List> discover(@Nonnull final Jenkins jenkins) { + private List> discover(@NonNull final Jenkins jenkins) { LOGGER.fine("Listing up @CLIMethod"); List> r = new ArrayList<>(); diff --git a/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java index 2c901b06b4..3c8a52d07e 100644 --- a/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java @@ -39,7 +39,7 @@ import org.kohsuke.args4j.spi.OptionHandler; import org.kohsuke.args4j.spi.Parameters; import org.kohsuke.args4j.spi.Setter; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Refers to {@link View} by its name. diff --git a/core/src/main/java/hudson/console/AnnotatedLargeText.java b/core/src/main/java/hudson/console/AnnotatedLargeText.java index 36d2664e92..9790bf8207 100644 --- a/core/src/main/java/hudson/console/AnnotatedLargeText.java +++ b/core/src/main/java/hudson/console/AnnotatedLargeText.java @@ -55,7 +55,7 @@ import com.jcraft.jzlib.GZIPInputStream; import com.jcraft.jzlib.GZIPOutputStream; import static java.lang.Math.abs; -import javax.annotation.CheckReturnValue; +import edu.umd.cs.findbugs.annotations.CheckReturnValue; import org.jenkinsci.remoting.util.AnonymousClassWarnings; /** diff --git a/core/src/main/java/hudson/console/ConsoleAnnotator.java b/core/src/main/java/hudson/console/ConsoleAnnotator.java index 9701eb302f..cc28c35452 100644 --- a/core/src/main/java/hudson/console/ConsoleAnnotator.java +++ b/core/src/main/java/hudson/console/ConsoleAnnotator.java @@ -30,8 +30,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.ListIterator; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Annotates one line of console output. @@ -82,7 +82,7 @@ public abstract class ConsoleAnnotator implements Serializable { * To indicate that you are not interested in the following lines, return {@code null}. */ @CheckForNull - public abstract ConsoleAnnotator annotate(@Nonnull T context, @Nonnull MarkupText text ); + public abstract ConsoleAnnotator annotate(@NonNull T context, @NonNull MarkupText text ); /** * Cast operation that restricts T. diff --git a/core/src/main/java/hudson/console/ConsoleLogFilter.java b/core/src/main/java/hudson/console/ConsoleLogFilter.java index 24b2c73c10..3ccdfd6c48 100644 --- a/core/src/main/java/hudson/console/ConsoleLogFilter.java +++ b/core/src/main/java/hudson/console/ConsoleLogFilter.java @@ -33,7 +33,7 @@ import hudson.model.Run; import hudson.tasks.BuildWrapper; import hudson.util.ArgumentListBuilder; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; @@ -102,7 +102,7 @@ public abstract class ConsoleLogFilter implements ExtensionPoint { * contextual decoration. * @since 1.632 */ - public OutputStream decorateLogger(@Nonnull Computer computer, OutputStream logger) throws IOException, InterruptedException { + public OutputStream decorateLogger(@NonNull Computer computer, OutputStream logger) throws IOException, InterruptedException { return logger; // by default no-op } diff --git a/core/src/main/java/hudson/console/ConsoleNote.java b/core/src/main/java/hudson/console/ConsoleNote.java index e55f7c2629..b4227b9707 100644 --- a/core/src/main/java/hudson/console/ConsoleNote.java +++ b/core/src/main/java/hudson/console/ConsoleNote.java @@ -32,8 +32,6 @@ import jenkins.model.Jenkins; import hudson.model.Run; import hudson.remoting.ObjectInputStreamEx; import hudson.util.IOUtils; -import hudson.util.UnbufferedBase64InputStream; -import org.apache.commons.codec.binary.Base64OutputStream; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.tools.ant.BuildListener; @@ -48,6 +46,7 @@ import java.io.Serializable; import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.Collection; import java.util.List; import com.jcraft.jzlib.GZIPInputStream; @@ -204,7 +203,7 @@ public abstract class ConsoleNote implements Serializable, Describable implements Serializable, Describable implements Serializable, Describable 0) { // new format IOUtils.skip(decoded, macSz); diff --git a/core/src/main/java/hudson/console/HudsonExceptionNote.java b/core/src/main/java/hudson/console/HudsonExceptionNote.java deleted file mode 100644 index 1c5f772665..0000000000 --- a/core/src/main/java/hudson/console/HudsonExceptionNote.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2010-2017, 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 hudson.console; - -import hudson.Extension; -import hudson.MarkupText; -import org.jenkinsci.Symbol; - -// TODO: the implementation has been deprecated due to JENKINS-42861 -// Consider providing alternate search mechanisms (JIRA, grepcode, etc.) as proposed in -// https://github.com/jenkinsci/jenkins/pull/2808#pullrequestreview-27467560 (JENKINS-43612) -/** - * Placed on the beginning of the exception stack trace produced by Jenkins, - * which in turn produces hyperlinked stack trace. - * - *

- * Exceptions in the user code (like junit etc) should be handled differently. This is only for exceptions - * that occur inside Jenkins. - * - * @author Kohsuke Kawaguchi - * @since 1.349 - produces search hyperlinks to the http://stacktrace.jenkins-ci.org service - * @since 2.56 - does nothing due to JENKINS-42861 - * @deprecated This ConsoleNote used to provide hyperlinks to the - * {@code http://stacktrace.jenkins-ci.org/} service, which is dead now (JENKINS-42861). - * This console note does nothing right now. - */ -@Deprecated -public class HudsonExceptionNote extends ConsoleNote { - - @Override - public ConsoleAnnotator annotate(Object context, MarkupText text, int charPos) { - - return null; - } - - @Extension @Symbol("stackTrace") - public static final class DescriptorImpl extends ConsoleAnnotationDescriptor { - @Override - public String getDisplayName() { - return "Exception Stack Trace"; - } - } -} diff --git a/core/src/main/java/hudson/console/ModelHyperlinkNote.java b/core/src/main/java/hudson/console/ModelHyperlinkNote.java index c389dfa383..a6e03015dc 100644 --- a/core/src/main/java/hudson/console/ModelHyperlinkNote.java +++ b/core/src/main/java/hudson/console/ModelHyperlinkNote.java @@ -6,7 +6,7 @@ import jenkins.model.Jenkins; import org.jenkinsci.Symbol; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * {@link HyperlinkNote} that links to a {@linkplain ModelObject model object}, @@ -25,7 +25,7 @@ public class ModelHyperlinkNote extends HyperlinkNote { return " class='model-link'"; } - public static String encodeTo(@Nonnull User u) { + public static String encodeTo(@NonNull User u) { return encodeTo(u,u.getDisplayName()); } @@ -54,6 +54,13 @@ public class ModelHyperlinkNote extends HyperlinkNote { return encodeTo("/computer/" + nodePath, node.getDisplayName()); } + /** + * @since TODO + */ + public static String encodeTo(Label label) { + return encodeTo("/" + label.getUrl(), label.getName()); + } + public static String encodeTo(String url, String text) { return HyperlinkNote.encodeTo(url, text, ModelHyperlinkNote::new); } diff --git a/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java b/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java index eb0c10584a..3f3cf3efb1 100644 --- a/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java +++ b/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java @@ -103,7 +103,7 @@ public final class MemoryUsageMonitor extends PeriodicWork { * Generates the memory usage statistics graph. */ public TrendChart doGraph(@QueryParameter String type) throws IOException { - Jenkins.get().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkAnyPermission(Jenkins.SYSTEM_READ, Jenkins.MANAGE); return MultiStageTimeSeries.createTrendChart(TimeScale.parse(type),used,max); } } diff --git a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java index 856cbb0cec..82dbffcf08 100644 --- a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java +++ b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java @@ -53,8 +53,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; import org.jenkinsci.Symbol; @@ -86,7 +86,7 @@ public class OldDataMonitor extends AdministrativeMonitor { * @throws IllegalStateException Monitor not found. * It should never happen since the monitor is located in the core. */ - @Nonnull + @NonNull static OldDataMonitor get(Jenkins j) throws IllegalStateException { return ExtensionList.lookupSingleton(OldDataMonitor.class); } @@ -440,6 +440,12 @@ public class OldDataMonitor extends AdministrativeMonitor { @Extension @Symbol("oldData") public static class ManagementLinkImpl extends ManagementLink { + @NonNull + @Override + public Category getCategory() { + return Category.TROUBLESHOOTING; + } + @Override public String getIconFileName() { return "document.png"; diff --git a/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java b/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java index 70c4c211bd..58d40bdea5 100644 --- a/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java +++ b/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java @@ -24,10 +24,14 @@ package hudson.diagnosis; import hudson.Extension; +import hudson.RestrictedSince; import hudson.Util; import hudson.model.AdministrativeMonitor; +import hudson.security.Permission; import jenkins.security.stapler.StaplerDispatchable; import org.jenkinsci.Symbol; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.stapler.HttpRedirect; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.HttpResponses; @@ -37,7 +41,7 @@ import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.Jenkins; -import org.kohsuke.stapler.Stapler; +import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.interceptor.RequirePOST; /** @@ -62,15 +66,31 @@ public class ReverseProxySetupMonitor extends AdministrativeMonitor { return true; } - public HttpResponse doTest() { - String referer = Stapler.getCurrentRequest().getReferer(); + @Restricted(DoNotUse.class) // WebOnly + @RestrictedSince("TODO since") + public HttpResponse doTest(StaplerRequest request, @QueryParameter boolean testWithContext) { + String referer = request.getReferer(); Jenkins j = Jenkins.get(); + String redirect; // May need to send an absolute URL, since handling of HttpRedirect with a relative URL does not currently honor X-Forwarded-Proto/Port at all. - String redirect = j.getRootUrl() + "administrativeMonitor/" + id + "/testForReverseProxySetup/" + (referer != null ? Util.rawEncode(referer) : "NO-REFERER") + "/"; + if (testWithContext) { + // Some of the possible values: "/jenkins" or "" + String contextPath = request.getServletContext().getContextPath(); + if (contextPath.startsWith("/")) { + // getRootUrl's contract is to end with /, we need to ensure the contextPath is not starting with one + // and as only the empty string does not contain a leading slash, we have to also add one at the end + contextPath = contextPath.substring(1) + "/"; + } + redirect = j.getRootUrl() + contextPath + "administrativeMonitor/" + id + "/testForReverseProxySetup/" + (referer != null ? Util.rawEncode(referer) : "NO-REFERER") + "/"; + } else { + redirect = j.getRootUrl() + "administrativeMonitor/" + id + "/testForReverseProxySetup/" + (referer != null ? Util.rawEncode(referer) : "NO-REFERER") + "/"; + } LOGGER.log(Level.FINE, "coming from {0} and redirecting to {1}", new Object[] {referer, redirect}); return new HttpRedirect(redirect); } + @Restricted(DoNotUse.class) // WebOnly + @RestrictedSince("TODO since") @StaplerDispatchable public void getTestForReverseProxySetup(String rest) { Jenkins j = Jenkins.get(); @@ -84,12 +104,20 @@ public class ReverseProxySetupMonitor extends AdministrativeMonitor { } } + @Override + public Permission getRequiredPermission() { + return Jenkins.SYSTEM_READ; + } + /** * Depending on whether the user said "yes" or "no", send him to the right place. */ + @Restricted(DoNotUse.class) // WebOnly + @RestrictedSince("TODO since") @RequirePOST public HttpResponse doAct(@QueryParameter String no) throws IOException { if(no!=null) { // dismiss + Jenkins.get().checkPermission(Jenkins.ADMINISTER); disable(true); // of course the irony is that this redirect won't work return HttpResponses.redirectViaContextPath("/manage"); diff --git a/core/src/main/java/hudson/diagnosis/TooManyJobsButNoView.java b/core/src/main/java/hudson/diagnosis/TooManyJobsButNoView.java index 56c9885415..93918ebfd5 100644 --- a/core/src/main/java/hudson/diagnosis/TooManyJobsButNoView.java +++ b/core/src/main/java/hudson/diagnosis/TooManyJobsButNoView.java @@ -24,6 +24,7 @@ package hudson.diagnosis; import hudson.model.AdministrativeMonitor; +import hudson.security.Permission; import jenkins.model.Jenkins; import hudson.Extension; import org.jenkinsci.Symbol; @@ -50,8 +51,12 @@ public class TooManyJobsButNoView extends AdministrativeMonitor { } public boolean isActivated() { - Jenkins h = Jenkins.get(); - return h.getViews().size()==1 && h.getItemMap().size()> THRESHOLD; + Jenkins j = Jenkins.get(); + if (j.hasPermission(Jenkins.ADMINISTER)) { + return j.getViews().size() == 1 && j.getItemMap().size() > THRESHOLD; + } + // SystemRead + return j.getViews().size() == 1 && j.getItems().size() > THRESHOLD; } /** @@ -59,6 +64,7 @@ public class TooManyJobsButNoView extends AdministrativeMonitor { */ @RequirePOST public void doAct(StaplerRequest req, StaplerResponse rsp) throws IOException { + Jenkins.get().checkPermission(Jenkins.ADMINISTER); if(req.hasParameter("no")) { disable(true); rsp.sendRedirect(req.getContextPath()+"/manage"); @@ -67,5 +73,10 @@ public class TooManyJobsButNoView extends AdministrativeMonitor { } } + @Override + public Permission getRequiredPermission() { + return Jenkins.SYSTEM_READ; + } + public static final int THRESHOLD = 16; } diff --git a/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java b/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java index e8c19e62a8..426ad5b7e1 100644 --- a/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java +++ b/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java @@ -69,7 +69,7 @@ public class WorkspaceSnapshotSCM extends SCM { /** * {@link Exception} indicating that the resolution of the job/permalink failed. */ - private final class ResolvedFailedException extends Exception { + private static final class ResolvedFailedException extends Exception { private ResolvedFailedException(String message) { super(message); } diff --git a/core/src/main/java/hudson/init/InitMilestone.java b/core/src/main/java/hudson/init/InitMilestone.java index 02fa2b6c23..4ee9fa26f4 100644 --- a/core/src/main/java/hudson/init/InitMilestone.java +++ b/core/src/main/java/hudson/init/InitMilestone.java @@ -89,14 +89,14 @@ public enum InitMilestone implements Milestone { /** * By this milestone, all the system configurations are loaded from file system - * @since TODO + * @since 2.220 */ SYSTEM_CONFIG_LOADED("System config loaded"), /** * By this milestone, the system configuration is adapted just in case any plugin (CasC might be an example) needs * to update configuration files - * @since TODO + * @since 2.220 */ SYSTEM_CONFIG_ADAPTED("System config adapted"), @@ -106,14 +106,15 @@ public enum InitMilestone implements Milestone { JOB_LOADED("Loaded all jobs"), /** - * By this milestone, any job configuration is adapted or updated just in case any plugin needs to update former/old configurations or init scripts - * @since TODO + * By this milestone, any job configuration is adapted or updated just in case any plugin needs to update former/old configurations. + * It does not include {@link hudson.init.impl.GroovyInitScript}s which get executed later + * @since 2.220 */ JOB_CONFIG_ADAPTED("Configuration for all jobs updated"), /** - * The very last milestone - * + * The very last milestone. + * All executions should be completed by it, including {@link hudson.init.impl.GroovyInitScript}s. * This is used in {@link Initializer#before()} since annotations cannot have null as the default value. */ COMPLETED("Completed initialization"); diff --git a/core/src/main/java/hudson/init/impl/GroovyInitScript.java b/core/src/main/java/hudson/init/impl/GroovyInitScript.java index 20cd1a481c..b0ec56ddf3 100644 --- a/core/src/main/java/hudson/init/impl/GroovyInitScript.java +++ b/core/src/main/java/hudson/init/impl/GroovyInitScript.java @@ -32,11 +32,12 @@ import static hudson.init.InitMilestone.*; /** * Run the initialization script, if it exists. + * It runs strictly after the initialization of other tasks during the last initialization milestone. * * @author Kohsuke Kawaguchi */ public class GroovyInitScript { - @Initializer(after=JOB_LOADED) + @Initializer(after=JOB_CONFIG_ADAPTED) public static void init(Jenkins j) { new GroovyHookScript("init", j.servletContext, j.getRootDir(), j.getPluginManager().uberClassLoader).run(); } diff --git a/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java b/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java index a8b73db22e..ccce1d159c 100644 --- a/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java +++ b/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java @@ -3,13 +3,23 @@ package hudson.init.impl; import hudson.init.Initializer; import jenkins.model.Jenkins; import jenkins.telemetry.impl.java11.MissingClassTelemetry; +import org.kohsuke.MetaInfServices; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.Stapler; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.WebApp; import org.kohsuke.stapler.compression.CompressionFilter; +import edu.umd.cs.findbugs.annotations.CheckForNull; import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.EOFException; import java.io.IOException; +import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -23,22 +33,7 @@ public class InstallUncaughtExceptionHandler { @Initializer public static void init(final Jenkins j) throws IOException { CompressionFilter.setUncaughtExceptionHandler(j.servletContext, (e, context, req, rsp) -> { - if (rsp.isCommitted()) { - LOGGER.log(isEOFException(e) ? Level.FINE : Level.WARNING, null, e); - return; - } - req.setAttribute("javax.servlet.error.exception",e); - try { - // If we have an exception, let's see if it's related with missing classes on Java 11. We reach - // here with a ClassNotFoundException in an action, for example. Setting the report here is the only - // way to catch the missing classes when the plugin uses Thread.currentThread().getContextClassLoader().loadClass - MissingClassTelemetry.reportExceptionInside(e); - WebApp.get(j.servletContext).getSomeStapler().invoke(req, rsp, j, "/oops"); - } catch (ServletException | IOException x) { - if (!Stapler.isSocketException(x)) { - throw x; - } - } + handleException(j, e, req, rsp, 500); }); try { Thread.setDefaultUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler()); @@ -54,6 +49,46 @@ public class InstallUncaughtExceptionHandler { } } + private static void handleException(Jenkins j, Throwable e, HttpServletRequest req, HttpServletResponse rsp, int code) throws IOException, ServletException { + if (rsp.isCommitted()) { + LOGGER.log(isEOFException(e) ? Level.FINE : Level.WARNING, null, e); + return; + } + String id = UUID.randomUUID().toString(); + LOGGER.log(isEOFException(e) ? Level.FINE : Level.WARNING, "Caught unhandled exception with ID " + id, e); + req.setAttribute("jenkins.exception.id", id); + req.setAttribute("javax.servlet.error.exception",e); + rsp.setStatus(code); + try { + // If we have an exception, let's see if it's related with missing classes on Java 11. We reach + // here with a ClassNotFoundException in an action, for example. Setting the report here is the only + // way to catch the missing classes when the plugin uses Thread.currentThread().getContextClassLoader().loadClass + MissingClassTelemetry.reportExceptionInside(e); + WebApp.get(j.servletContext).getSomeStapler().invoke(req, rsp, j, "/oops"); + } catch (ServletException | IOException x) { + if (!Stapler.isSocketException(x)) { + throw x; + } + } + } + + @Restricted(NoExternalUse.class) + @MetaInfServices + public static class ErrorCustomizer implements HttpResponses.ErrorCustomizer { + @CheckForNull + @Override + public HttpResponses.HttpResponseException handleError(int code, Throwable cause) { + if (Jenkins.getInstanceOrNull() == null) { + return null; + } + return new HttpResponses.HttpResponseException(cause) { + public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException { + handleException(Jenkins.get(), cause, req, rsp, code); + } + }; + } + } + private static boolean isEOFException(Throwable e) { if (e == null) { return false; diff --git a/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java b/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java index a8f412539a..92a75d850f 100644 --- a/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java +++ b/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java @@ -53,6 +53,7 @@ import org.apache.tools.ant.DefaultLogger; import org.apache.tools.ant.types.FileSet; import org.kohsuke.stapler.interceptor.RequirePOST; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import java.io.File; import java.io.IOException; @@ -100,6 +101,13 @@ public class WindowsInstallerLink extends ManagementLink { return Messages.WindowsInstallerLink_Description(); } + + @NonNull + @Override + public Category getCategory() { + return Category.CONFIGURATION; + } + /** * Is the installation successful? */ @@ -119,7 +127,7 @@ public class WindowsInstallerLink extends ManagementLink { sendError("Installation is already complete",req,rsp); return; } - if(!DotNet.isInstalled(2,0)) { + if(!DotNet.isInstalled(4,0) && !DotNet.isInstalled(2,0)) { sendError(".NET Framework 2.0 or later is required for this feature",req,rsp); return; } diff --git a/core/src/main/java/hudson/logging/LogRecorder.java b/core/src/main/java/hudson/logging/LogRecorder.java index c6ec3e1b41..a2a233df07 100644 --- a/core/src/main/java/hudson/logging/LogRecorder.java +++ b/core/src/main/java/hudson/logging/LogRecorder.java @@ -68,7 +68,7 @@ import org.kohsuke.stapler.verb.POST; * TODO: still a work in progress. * *

Access Control

- * {@link LogRecorder} is only visible for administrators, and this access control happens at + * {@link LogRecorder} is only visible for administrators and system readers, and this access control happens at * {@link jenkins.model.Jenkins#getLog()}, the sole entry point for binding {@link LogRecorder} to URL. * * @author Kohsuke Kawaguchi @@ -332,6 +332,8 @@ public class LogRecorder extends AbstractModelObject implements Saveable { */ @POST public synchronized void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { + Jenkins.get().checkPermission(Jenkins.ADMINISTER); + JSONObject src = req.getSubmittedForm(); String newName = src.getString("name"), redirect = "."; @@ -358,6 +360,8 @@ public class LogRecorder extends AbstractModelObject implements Saveable { @RequirePOST public HttpResponse doClear() throws IOException { + Jenkins.get().checkPermission(Jenkins.ADMINISTER); + handler.clear(); return HttpResponses.redirectToDot(); } @@ -385,6 +389,8 @@ public class LogRecorder extends AbstractModelObject implements Saveable { */ @RequirePOST public synchronized void doDoDelete(StaplerResponse rsp) throws IOException, ServletException { + Jenkins.get().checkPermission(Jenkins.ADMINISTER); + getConfigFile().delete(); getParent().logRecorders.remove(name); // Disable logging for all our targets, diff --git a/core/src/main/java/hudson/logging/LogRecorderManager.java b/core/src/main/java/hudson/logging/LogRecorderManager.java index 4bf9730f3a..c74159049d 100644 --- a/core/src/main/java/hudson/logging/LogRecorderManager.java +++ b/core/src/main/java/hudson/logging/LogRecorderManager.java @@ -112,6 +112,8 @@ public class LogRecorderManager extends AbstractModelObject implements ModelObje */ @RequirePOST public HttpResponse doNewLogRecorder(@QueryParameter String name) { + Jenkins.get().checkPermission(Jenkins.ADMINISTER); + Jenkins.checkGoodName(name); logRecorders.put(name,new LogRecorder(name)); @@ -208,7 +210,7 @@ public class LogRecorderManager extends AbstractModelObject implements ModelObje @Restricted(NoExternalUse.class) public Object getTarget() { if (!SKIP_PERMISSION_CHECK) { - Jenkins.get().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(Jenkins.SYSTEM_READ); } return this; } diff --git a/core/src/main/java/hudson/markup/MarkupFormatter.java b/core/src/main/java/hudson/markup/MarkupFormatter.java index be8004136a..5bec9ab0a3 100644 --- a/core/src/main/java/hudson/markup/MarkupFormatter.java +++ b/core/src/main/java/hudson/markup/MarkupFormatter.java @@ -29,8 +29,8 @@ import hudson.util.HttpResponses; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.QueryParameter; @@ -69,9 +69,9 @@ public abstract class MarkupFormatter extends AbstractDescribableImpl,R extends Abs * Normally, a workspace is assigned by {@link hudson.model.Run.RunExecution}, but this lets you set the workspace in case * {@link AbstractBuild} is created without a build. */ - protected void setWorkspace(@Nonnull FilePath ws) { + protected void setWorkspace(@NonNull FilePath ws) { this.workspace = ws.getRemote(); } @@ -326,7 +326,7 @@ public abstract class AbstractBuild

,R extends Abs @Override @Exported - @Nonnull public Set getCulprits() { + @NonNull public Set getCulprits() { return RunWithSCM.super.getCulprits(); } @@ -336,7 +336,7 @@ public abstract class AbstractBuild

,R extends Abs } @Override - @Nonnull + @NonNull public Set calculateCulprits() { Set c = RunWithSCM.super.calculateCulprits(); @@ -404,7 +404,7 @@ public abstract class AbstractBuild

,R extends Abs * @return Returns the current {@link Node} * @throws IllegalStateException if that cannot be determined */ - protected final @Nonnull Node getCurrentNode() throws IllegalStateException { + protected final @NonNull Node getCurrentNode() throws IllegalStateException { Executor exec = Executor.currentExecutor(); if (exec == null) { throw new IllegalStateException("not being called from an executor thread"); @@ -433,7 +433,7 @@ public abstract class AbstractBuild

,R extends Abs * @param wsl * Passed in for the convenience. The returned path must be registered to this object. */ - protected Lease decideWorkspace(@Nonnull Node n, WorkspaceList wsl) throws InterruptedException, IOException { + protected Lease decideWorkspace(@NonNull Node n, WorkspaceList wsl) throws InterruptedException, IOException { String customWorkspace = getProject().getCustomWorkspace(); if (customWorkspace != null) { FilePath rootPath = n.getRootPath(); @@ -451,7 +451,7 @@ public abstract class AbstractBuild

,R extends Abs return wsl.allocate(ws, getBuild()); } - public Result run(@Nonnull BuildListener listener) throws Exception { + public Result run(@NonNull BuildListener listener) throws Exception { final Node node = getCurrentNode(); assert builtOn==null; @@ -525,8 +525,8 @@ public abstract class AbstractBuild

,R extends Abs * @param listener * Always non-null. Connected to the main build output. */ - @Nonnull - protected Launcher createLauncher(@Nonnull BuildListener listener) throws IOException, InterruptedException { + @NonNull + protected Launcher createLauncher(@NonNull BuildListener listener) throws IOException, InterruptedException { final Node currentNode = getCurrentNode(); Launcher l = currentNode.createLauncher(listener); @@ -808,7 +808,7 @@ public abstract class AbstractBuild

,R extends Abs * @return never null. */ @Exported - @Nonnull public ChangeLogSet getChangeSet() { + @NonNull public ChangeLogSet getChangeSet() { synchronized (changeSetLock) { if (scm==null) { scm = NullChangeLogParser.INSTANCE; @@ -833,7 +833,7 @@ public abstract class AbstractBuild

,R extends Abs } @Override - @Nonnull public List> getChangeSets() { + @NonNull public List> getChangeSets() { ChangeLogSet cs = getChangeSet(); return cs.isEmptySet() ? Collections.emptyList() : Collections.singletonList(cs); } @@ -865,7 +865,10 @@ public abstract class AbstractBuild

,R extends Abs FilePath ws = getWorkspace(); if (ws != null) { // if this is done very early on in the build, workspace may not be decided yet. see HUDSON-3997 env.put("WORKSPACE", ws.getRemote()); - env.put("WORKSPACE_TMP", WorkspaceList.tempDir(ws).getRemote()); // JENKINS-60634 + FilePath tempDir = WorkspaceList.tempDir(ws); + if (tempDir != null) { + env.put("WORKSPACE_TMP", tempDir.getRemote()); // JENKINS-60634 + } } project.getScm().buildEnvVars(this,env); diff --git a/core/src/main/java/hudson/model/AbstractCIBase.java b/core/src/main/java/hudson/model/AbstractCIBase.java index f6c11f0219..ab158052fd 100644 --- a/core/src/main/java/hudson/model/AbstractCIBase.java +++ b/core/src/main/java/hudson/model/AbstractCIBase.java @@ -33,14 +33,11 @@ import hudson.slaves.RetentionStrategy; import jenkins.model.Jenkins; import org.kohsuke.stapler.StaplerFallback; import org.kohsuke.stapler.StaplerProxy; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; import java.util.*; -import java.util.concurrent.CopyOnWriteArraySet; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import jenkins.model.Configuration; @@ -89,17 +86,31 @@ public abstract class AbstractCIBase extends Node implements ItemGroup disabledAdministrativeMonitors = new HashSet<>(); - /*package*/ final CopyOnWriteArraySet disabledAdministrativeMonitors = new CopyOnWriteArraySet<>(); - - @Restricted(NoExternalUse.class) - public CopyOnWriteArraySet getDisabledAdministrativeMonitors(){ - return disabledAdministrativeMonitors; + /** + * Get the disabled administrative monitors + * + * @since TODO + */ + public Set getDisabledAdministrativeMonitors(){ + synchronized (this.disabledAdministrativeMonitors) { + return new HashSet<>(disabledAdministrativeMonitors); + } } - + + /** + * Set the disabled administrative monitors + * + * @since TODO + */ + public void setDisabledAdministrativeMonitors(Set disabledAdministrativeMonitors) { + synchronized (this.disabledAdministrativeMonitors) { + this.disabledAdministrativeMonitors.clear(); + this.disabledAdministrativeMonitors.addAll(disabledAdministrativeMonitors); + } + } + /* ================================================================================================================= * Implementation provided * ============================================================================================================== */ diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java index d00fb66986..17025e8772 100644 --- a/core/src/main/java/hudson/model/AbstractItem.java +++ b/core/src/main/java/hudson/model/AbstractItem.java @@ -73,7 +73,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.acegisecurity.AccessDeniedException; import org.kohsuke.stapler.HttpResponse; @@ -204,7 +204,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet * This bridge method is to maintain binary compatibility with {@link TopLevelItem#getParent()}. */ @WithBridgeMethods(value=Jenkins.class,castRequired=true) - @Override public @Nonnull ItemGroup getParent() { + @Override public @NonNull ItemGroup getParent() { if (parent == null) { throw new IllegalStateException("no parent set on " + getClass().getName() + "[" + name + "]"); } @@ -273,7 +273,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet * {@link FormValidation#error} with a message explaining the problem. */ @Restricted(NoExternalUse.class) - public @Nonnull FormValidation doCheckNewName(@QueryParameter String newName) { + public @NonNull FormValidation doCheckNewName(@QueryParameter String newName) { if (!isNameEditable()) { return FormValidation.error("Trying to rename an item that does not support this operation."); @@ -307,7 +307,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet * Check new name for job * @param newName - New name for job. */ - private void checkIfNameIsUsed(@Nonnull String newName) throws Failure { + private void checkIfNameIsUsed(@NonNull String newName) throws Failure { try { Item item = getParent().getItem(newName); if (item != null) { @@ -345,7 +345,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet * @since 2.110 * @see Job#checkRename */ - protected void checkRename(@Nonnull String newName) throws Failure { + protected void checkRename(@NonNull String newName) throws Failure { } @@ -673,7 +673,8 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet public void delete( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { try { - doDoDelete(req,rsp); + delete(); + rsp.setStatus(204); } catch (InterruptedException e) { // TODO: allow this in Stapler throw new ServletException(e); diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index 00a82dbf69..c3dad5d065 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -95,8 +95,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import jenkins.model.BlockedBecauseOfBuildInProgress; import jenkins.model.Jenkins; @@ -1019,7 +1019,7 @@ public abstract class AbstractProject

,R extends A */ @Deprecated public static class BecauseOfBuildInProgress extends BlockedBecauseOfBuildInProgress { - public BecauseOfBuildInProgress(@Nonnull AbstractBuild build) { + public BecauseOfBuildInProgress(@NonNull AbstractBuild build) { super(build); } } @@ -1391,7 +1391,7 @@ public abstract class AbstractProject

,R extends A } } - private PollingResult pollWithWorkspace(TaskListener listener, SCM scm, R lb, @Nonnull FilePath ws, WorkspaceList l) throws InterruptedException, IOException { + private PollingResult pollWithWorkspace(TaskListener listener, SCM scm, R lb, @NonNull FilePath ws, WorkspaceList l) throws InterruptedException, IOException { // if doing non-concurrent build, acquire a workspace in a way that causes builds to block for this workspace. // this prevents multiple workspaces of the same job --- the behavior of Hudson < 1.319. // @@ -1922,7 +1922,7 @@ public abstract class AbstractProject

,R extends A * @param project May be specified to perform project specific validation. * @since 1.590 */ - public static @Nonnull FormValidation validateLabelExpression(String value, @CheckForNull AbstractProject project) { + public static @NonNull FormValidation validateLabelExpression(String value, @CheckForNull AbstractProject project) { if (Util.fixEmpty(value)==null) return FormValidation.ok(); // nothing typed yet try { @@ -2137,8 +2137,8 @@ public abstract class AbstractProject

,R extends A * @param label the label that the project wants to restrict itself to. * @return the {@link FormValidation} result. */ - @Nonnull - public abstract FormValidation check(@Nonnull AbstractProject project, @Nonnull Label label); + @NonNull + public abstract FormValidation check(@NonNull AbstractProject project, @NonNull Label label); } } diff --git a/core/src/main/java/hudson/model/Action.java b/core/src/main/java/hudson/model/Action.java index 72d2f84e06..0b80bc36fb 100644 --- a/core/src/main/java/hudson/model/Action.java +++ b/core/src/main/java/hudson/model/Action.java @@ -25,7 +25,7 @@ package hudson.model; import hudson.Functions; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Object that contributes additional information, behaviors, and UIs to {@link ModelObject} diff --git a/core/src/main/java/hudson/model/Actionable.java b/core/src/main/java/hudson/model/Actionable.java index 174bf192ff..17c5fa3b8e 100644 --- a/core/src/main/java/hudson/model/Actionable.java +++ b/core/src/main/java/hudson/model/Actionable.java @@ -32,8 +32,8 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import jenkins.model.ModelObjectWithContextMenu; import jenkins.model.TransientActionFactory; import org.kohsuke.stapler.StaplerRequest; @@ -72,7 +72,7 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj * May still be called for compatibility reasons from subclasses predating {@link TransientActionFactory}. */ @Deprecated - @Nonnull + @NonNull public List getActions() { //this double checked synchronization is only safe if the field 'actions' is volatile if (actions == null) { @@ -92,7 +92,7 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj * @since 1.548 */ @Exported(name="actions") - @Nonnull + @NonNull public final List getAllActions() { List _actions = getActions(); boolean adding = false; @@ -132,7 +132,7 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj * @return an unmodifiable, possible empty list * @see #getAction(Class) */ - @Nonnull + @NonNull public List getActions(Class type) { List _actions = Util.filter(getActions(), type); for (TransientActionFactory taf : TransientActionFactory.factoriesFor(getClass(), type)) { @@ -148,7 +148,7 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj */ @SuppressWarnings({"ConstantConditions","deprecation"}) @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE") - public void addAction(@Nonnull Action a) { + public void addAction(@NonNull Action a) { if(a==null) { throw new IllegalArgumentException("Action must be non-null"); } @@ -172,7 +172,7 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj */ @SuppressWarnings({"ConstantConditions", "deprecation"}) @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE") - public void replaceAction(@Nonnull Action a) { + public void replaceAction(@NonNull Action a) { addOrReplaceAction(a); } @@ -191,7 +191,7 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj */ @SuppressWarnings({"ConstantConditions", "deprecation"}) @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE") - public boolean addOrReplaceAction(@Nonnull Action a) { + public boolean addOrReplaceAction(@NonNull Action a) { if (a == null) { throw new IllegalArgumentException("Action must be non-null"); } @@ -250,7 +250,7 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj */ @SuppressWarnings({"ConstantConditions","deprecation"}) @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE") - public boolean removeActions(@Nonnull Class clazz) { + public boolean removeActions(@NonNull Class clazz) { if (clazz == null) { throw new IllegalArgumentException("Action type must be non-null"); } @@ -282,7 +282,7 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj */ @SuppressWarnings({"ConstantConditions", "deprecation"}) @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE") - public boolean replaceActions(@Nonnull Class clazz, @Nonnull Action a) { + public boolean replaceActions(@NonNull Class clazz, @NonNull Action a) { if (clazz == null) { throw new IllegalArgumentException("Action type must be non-null"); } diff --git a/core/src/main/java/hudson/model/AdministrativeMonitor.java b/core/src/main/java/hudson/model/AdministrativeMonitor.java index 4554809c54..9d2214daba 100644 --- a/core/src/main/java/hudson/model/AdministrativeMonitor.java +++ b/core/src/main/java/hudson/model/AdministrativeMonitor.java @@ -27,6 +27,7 @@ import hudson.ExtensionPoint; import hudson.ExtensionList; import hudson.Extension; import hudson.ExtensionPoint.LegacyInstancesAreScopedToHudson; +import hudson.security.Permission; import hudson.triggers.SCMTrigger; import hudson.triggers.TimerTrigger; @@ -74,6 +75,14 @@ import org.kohsuke.stapler.interceptor.RequirePOST; * * * + *

Use with System Read permission

+ *

+ * By default administrative monitors are visible only to users with Administer permission. + * Users with {@link Jenkins#SYSTEM_READ} permission can access administrative monitors that override {@link #getRequiredPermission()}. + * Care needs to be taken to ensure users with that permission don't have access to actions modifying system state. + * For more details, see {@link #getRequiredPermission()}. + *

+ * * @author Kohsuke Kawaguchi * @since 1.273 * @see Jenkins#administrativeMonitors @@ -118,8 +127,12 @@ public abstract class AdministrativeMonitor extends AbstractModelObject implemen public void disable(boolean value) throws IOException { AbstractCIBase jenkins = Jenkins.get(); Set set = jenkins.getDisabledAdministrativeMonitors(); - if(value) set.add(id); - else set.remove(id); + if (value) { + set.add(id); + } else { + set.remove(id); + } + jenkins.setDisabledAdministrativeMonitors(set); jenkins.save(); } @@ -149,16 +162,37 @@ public abstract class AdministrativeMonitor extends AbstractModelObject implemen */ @RequirePOST public void doDisable(StaplerRequest req, StaplerResponse rsp) throws IOException { + Jenkins.get().checkPermission(Jenkins.ADMINISTER); disable(true); rsp.sendRedirect2(req.getContextPath()+"/manage"); } /** - * Requires ADMINISTER permission for any operation in here. + * Required permission to view this admin monitor. + * By default {@link Jenkins#ADMINISTER}, but {@link Jenkins#SYSTEM_READ} is also supported. + *

+ * Changing this permission check to return {@link Jenkins#SYSTEM_READ} will make the active + * administrative monitor appear on {@code manage.jelly} and on the globally visible + * {@link jenkins.management.AdministrativeMonitorsDecorator} to users without Administer permission. + * {@link #doDisable(StaplerRequest, StaplerResponse)} will still always require Administer permission. + *

+ *

+ * Implementers need to ensure that {@code doAct} and other web methods perform necessary permission checks: + * Users with System Read permissions are expected to be limited to read-only access. + * Form UI elements that change system state, e.g. toggling a feature on or off, need to be hidden from users + * lacking Administer permission. + *

+ */ + public Permission getRequiredPermission() { + return Jenkins.ADMINISTER; + } + + /** + * Ensure that URLs in this administrative monitor are only accessible to users with {@link #getRequiredPermission()}. */ @Restricted(NoExternalUse.class) public Object getTarget() { - Jenkins.get().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(getRequiredPermission()); return this; } diff --git a/core/src/main/java/hudson/model/AllView.java b/core/src/main/java/hudson/model/AllView.java index 4daedafd5a..cdcb633f10 100644 --- a/core/src/main/java/hudson/model/AllView.java +++ b/core/src/main/java/hudson/model/AllView.java @@ -27,7 +27,7 @@ import java.util.List; import java.util.Locale; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.util.SystemProperties; import org.apache.commons.lang.StringUtils; import org.jenkinsci.Symbol; @@ -133,9 +133,9 @@ public class AllView extends View { * matching name is detected, in which case this will be the new name of the primary view. * @since 2.37 */ - @Nonnull - public static String migrateLegacyPrimaryAllViewLocalizedName(@Nonnull List views, - @Nonnull String primaryView) { + @NonNull + public static String migrateLegacyPrimaryAllViewLocalizedName(@NonNull List views, + @NonNull String primaryView) { if (DEFAULT_VIEW_NAME.equals(primaryView)) { // modern name, we are safe return primaryView; diff --git a/core/src/main/java/hudson/model/AutoCompletionCandidates.java b/core/src/main/java/hudson/model/AutoCompletionCandidates.java index 735a9b80e8..a54c6d6c7c 100644 --- a/core/src/main/java/hudson/model/AutoCompletionCandidates.java +++ b/core/src/main/java/hudson/model/AutoCompletionCandidates.java @@ -37,7 +37,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import org.apache.commons.lang.StringUtils; /** diff --git a/core/src/main/java/hudson/model/Build.java b/core/src/main/java/hudson/model/Build.java index 00f9c21d4e..da3891307b 100644 --- a/core/src/main/java/hudson/model/Build.java +++ b/core/src/main/java/hudson/model/Build.java @@ -43,7 +43,7 @@ import java.util.logging.Logger; import java.util.logging.Level; import static hudson.model.Result.FAILURE; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * A build of a {@link Project}. @@ -139,7 +139,7 @@ public abstract class Build

,B extends Build> deprecated class here. */ - protected Result doRun(@Nonnull BuildListener listener) throws Exception { + protected Result doRun(@NonNull BuildListener listener) throws Exception { if(!preBuild(listener,project.getBuilders())) return FAILURE; if(!preBuild(listener,project.getPublishersList())) @@ -182,7 +182,7 @@ public abstract class Build

,B extends Build> return r; } - public void post2(@Nonnull BuildListener listener) throws IOException, InterruptedException { + public void post2(@NonNull BuildListener listener) throws IOException, InterruptedException { if (!performAllBuildSteps(listener, project.getPublishersList(), true)) setResult(FAILURE); if (!performAllBuildSteps(listener, project.getProperties(), true)) @@ -190,7 +190,7 @@ public abstract class Build

,B extends Build> } @Override - public void cleanUp(@Nonnull BuildListener listener) throws Exception { + public void cleanUp(@NonNull BuildListener listener) throws Exception { // at this point it's too late to mark the build as a failure, so ignore return value. try { performAllBuildSteps(listener, project.getPublishersList(), false); @@ -201,7 +201,7 @@ public abstract class Build

,B extends Build> super.cleanUp(listener); } - private boolean build(@Nonnull BuildListener listener, @Nonnull Collection steps) throws IOException, InterruptedException { + private boolean build(@NonNull BuildListener listener, @NonNull Collection steps) throws IOException, InterruptedException { for( BuildStep bs : steps ) { if(!perform(bs,listener)) { LOGGER.log(Level.FINE, "{0} : {1} failed", new Object[] {Build.this, bs}); diff --git a/core/src/main/java/hudson/model/Cause.java b/core/src/main/java/hudson/model/Cause.java index 08f6655236..c598829fee 100644 --- a/core/src/main/java/hudson/model/Cause.java +++ b/core/src/main/java/hudson/model/Cause.java @@ -41,8 +41,8 @@ import java.io.IOException; import java.util.HashSet; import java.util.Objects; import java.util.Set; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Cause object base class. This class hierarchy is used to keep track of why @@ -76,7 +76,7 @@ public abstract class Cause { * Called when the cause is registered. * @since 1.568 */ - public void onAddedTo(@Nonnull Run build) { + public void onAddedTo(@NonNull Run build) { if (build instanceof AbstractBuild) { onAddedTo((AbstractBuild) build); } @@ -95,13 +95,13 @@ public abstract class Cause { * this ought to be {@code transient}. * @since 1.568 */ - public void onLoad(@Nonnull Run build) { + public void onLoad(@NonNull Run build) { if (build instanceof AbstractBuild) { onLoad((AbstractBuild) build); } } - void onLoad(@Nonnull Job job, int buildNumber) { + void onLoad(@NonNull Job job, int buildNumber) { Run build = job.getBuildByNumber(buildNumber); if (build != null) { onLoad(build); @@ -160,7 +160,7 @@ public abstract class Cause { */ @Deprecated private transient Cause upstreamCause; - private @Nonnull List upstreamCauses; + private @NonNull List upstreamCauses; /** * @deprecated since 2009-02-28 @@ -182,7 +182,7 @@ public abstract class Cause { } } - private UpstreamCause(String upstreamProject, int upstreamBuild, String upstreamUrl, @Nonnull List upstreamCauses) { + private UpstreamCause(String upstreamProject, int upstreamBuild, String upstreamUrl, @NonNull List upstreamCauses) { this.upstreamProject = upstreamProject; this.upstreamBuild = upstreamBuild; this.upstreamUrl = upstreamUrl; @@ -190,7 +190,7 @@ public abstract class Cause { } @Override - public void onLoad(@Nonnull Job _job, int _buildNumber) { + public void onLoad(@NonNull Job _job, int _buildNumber) { Item i = Jenkins.get().getItemByFullName(this.upstreamProject); if (!(i instanceof Job)) { // cannot initialize upstream causes @@ -229,7 +229,7 @@ public abstract class Cause { return Objects.hash(upstreamCauses, upstreamBuild, upstreamUrl, upstreamProject); } - private @Nonnull Cause trim(@Nonnull Cause c, int depth, Set traversed) { + private @NonNull Cause trim(@NonNull Cause c, int depth, Set traversed) { if (!(c instanceof UpstreamCause)) { return c; } @@ -347,7 +347,7 @@ public abstract class Cause { @Override public String toString() { return "JENKINS-14814"; } - @Override public void onLoad(@Nonnull Job _job, int _buildNumber) {} + @Override public void onLoad(@NonNull Job _job, int _buildNumber) {} } } @@ -426,7 +426,7 @@ public abstract class Cause { return userId; } - @Nonnull + @NonNull private String getUserIdOrUnknown() { return userId != null ? userId : User.getUnknown().getId(); } diff --git a/core/src/main/java/hudson/model/CheckPoint.java b/core/src/main/java/hudson/model/CheckPoint.java index ab4e176f6f..ebb78223b9 100644 --- a/core/src/main/java/hudson/model/CheckPoint.java +++ b/core/src/main/java/hudson/model/CheckPoint.java @@ -27,7 +27,7 @@ import hudson.tasks.BuildStep; import hudson.tasks.Recorder; import hudson.tasks.Builder; import hudson.scm.SCM; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Provides a mechanism for synchronizing build executions in the face of concurrent builds. @@ -153,7 +153,7 @@ public final class CheckPoint { * @throws InterruptedException if the build is aborted while waiting * @since 1.528 */ - public void block(@Nonnull BuildListener listener, @Nonnull String waiter) throws InterruptedException { + public void block(@NonNull BuildListener listener, @NonNull String waiter) throws InterruptedException { Run.waitForCheckpoint(this, listener, waiter); } diff --git a/core/src/main/java/hudson/model/ChoiceParameterDefinition.java b/core/src/main/java/hudson/model/ChoiceParameterDefinition.java index 612925bce4..ab27de85a1 100644 --- a/core/src/main/java/hudson/model/ChoiceParameterDefinition.java +++ b/core/src/main/java/hudson/model/ChoiceParameterDefinition.java @@ -13,9 +13,9 @@ import org.apache.commons.lang.StringUtils; import net.sf.json.JSONObject; import hudson.Extension; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Arrays; @@ -38,19 +38,19 @@ public class ChoiceParameterDefinition extends SimpleParameterDefinition { return !StringUtils.isEmpty(strippedChoices) && strippedChoices.split(CHOICES_DELIMITER).length > 0; } - public ChoiceParameterDefinition(@Nonnull String name, @Nonnull String choices, String description) { + public ChoiceParameterDefinition(@NonNull String name, @NonNull String choices, String description) { super(name, description); setChoicesText(choices); defaultValue = null; } - public ChoiceParameterDefinition(@Nonnull String name, @Nonnull String[] choices, String description) { + public ChoiceParameterDefinition(@NonNull String name, @NonNull String[] choices, String description) { super(name, description); this.choices = new ArrayList<>(Arrays.asList(choices)); defaultValue = null; } - private ChoiceParameterDefinition(@Nonnull String name, @Nonnull List choices, String defaultValue, String description) { + private ChoiceParameterDefinition(@NonNull String name, @NonNull List choices, String defaultValue, String description) { super(name, description); this.choices = choices; this.defaultValue = defaultValue; @@ -174,7 +174,7 @@ public class ChoiceParameterDefinition extends SimpleParameterDefinition { /* * We need this for JENKINS-26143 -- reflective creation cannot handle setChoices(Object). See that method for context. */ - public ParameterDefinition newInstance(@Nullable StaplerRequest req, @Nonnull JSONObject formData) throws FormException { + public ParameterDefinition newInstance(@Nullable StaplerRequest req, @NonNull JSONObject formData) throws FormException { String name = formData.getString("name"); String desc = formData.getString("description"); String choiceText = formData.getString("choices"); diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 0fe348beda..073af962c8 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -92,8 +92,8 @@ import org.kohsuke.stapler.export.ExportedBean; import org.kohsuke.stapler.interceptor.RequirePOST; import org.kohsuke.stapler.verb.POST; -import javax.annotation.OverridingMethodsMustInvokeSuper; -import javax.annotation.concurrent.GuardedBy; +import edu.umd.cs.findbugs.annotations.OverrideMustInvoke; +import net.jcip.annotations.GuardedBy; import javax.servlet.ServletException; import java.io.File; @@ -118,9 +118,9 @@ import java.net.Inet4Address; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import static javax.servlet.http.HttpServletResponse.*; @@ -275,7 +275,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces @SuppressWarnings({"ConstantConditions","deprecation"}) @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE") @Override - public void addAction(@Nonnull Action a) { + public void addAction(@NonNull Action a) { if (a == null) { throw new IllegalArgumentException("Action must be non-null"); } @@ -290,7 +290,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces * @see #getLogDir() * @see #relocateOldLogs() */ - public @Nonnull File getLogFile() { + public @NonNull File getLogFile() { return new File(getLogDir(),"slave.log"); } @@ -301,7 +301,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces * * @since 1.613 */ - protected @Nonnull File getLogDir() { + protected @NonNull File getLogDir() { File dir = new File(Jenkins.get().getRootDir(),"logs/slaves/"+nodeName); try { IOUtils.mkdirs(dir); @@ -563,7 +563,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces /** * Returns {@link Node#getNodeName() the name of the node}. */ - public @Nonnull String getName() { + public @NonNull String getName() { return nodeName != null ? nodeName : ""; } @@ -763,7 +763,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces } @Exported - @Override public @Nonnull String getDisplayName() { + @Override public @NonNull String getDisplayName() { return nodeName; } @@ -1074,7 +1074,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces */ @Restricted(DoNotUse.class) @Exported - public @Nonnull String getDescription() { + public @NonNull String getDescription() { Node node = getNode(); return (node != null) ? node.getNodeDescription() : ""; } @@ -1197,7 +1197,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces * @see ProcStarter#envs(Map) * @since 1.489 */ - public @Nonnull EnvVars buildEnvironment(@Nonnull TaskListener listener) throws IOException, InterruptedException { + public @NonNull EnvVars buildEnvironment(@NonNull TaskListener listener) throws IOException, InterruptedException { EnvVars env = new EnvVars(); Node node = getNode(); @@ -1383,7 +1383,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces * Retrieve the RSS feed for the last build for each project executed in this computer. * Only the information from {@link AbstractProject} is displayed since there isn't a proper API to gather * information about the node where the builds are executed for other sorts of projects such as Pipeline - * @since TODO + * @since 2.215 */ @Restricted(DoNotUse.class) public void doRssLatest( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { @@ -1626,7 +1626,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces * @see hudson.slaves.RetentionStrategy#isAcceptingTasks(Computer) * @see hudson.model.Node#isAcceptingTasks() */ - @OverridingMethodsMustInvokeSuper + @OverrideMustInvoke public boolean isAcceptingTasks() { final Node node = getNode(); return getRetentionStrategy().isAcceptingTasks(this) && (node == null || node.isAcceptingTasks()); @@ -1698,31 +1698,31 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces @Restricted(NoExternalUse.class) public static class DisplayExecutor implements ModelObject { - @Nonnull + @NonNull private final String displayName; - @Nonnull + @NonNull private final String url; - @Nonnull + @NonNull private final Executor executor; - public DisplayExecutor(@Nonnull String displayName, @Nonnull String url, @Nonnull Executor executor) { + public DisplayExecutor(@NonNull String displayName, @NonNull String url, @NonNull Executor executor) { this.displayName = displayName; this.url = url; this.executor = executor; } @Override - @Nonnull + @NonNull public String getDisplayName() { return displayName; } - @Nonnull + @NonNull public String getUrl() { return url; } - @Nonnull + @NonNull public Executor getExecutor() { return executor; } diff --git a/core/src/main/java/hudson/model/ComputerSet.java b/core/src/main/java/hudson/model/ComputerSet.java index 7fafbf68c7..151221621a 100644 --- a/core/src/main/java/hudson/model/ComputerSet.java +++ b/core/src/main/java/hudson/model/ComputerSet.java @@ -49,7 +49,7 @@ import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import org.kohsuke.stapler.interceptor.RequirePOST; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import java.io.File; import java.io.IOException; @@ -416,7 +416,7 @@ public final class ComputerSet extends AbstractModelObject implements Describabl * @return The list of strings of computer names (excluding master) * @since 2.14 */ - @Nonnull + @NonNull public static List getComputerNames() { final ArrayList names = new ArrayList<>(); for (Computer c : Jenkins.get().getComputers()) { diff --git a/core/src/main/java/hudson/model/Descriptor.java b/core/src/main/java/hudson/model/Descriptor.java index a946de2acf..7ed163adf6 100644 --- a/core/src/main/java/hudson/model/Descriptor.java +++ b/core/src/main/java/hudson/model/Descriptor.java @@ -24,7 +24,6 @@ package hudson.model; import hudson.DescriptorExtensionList; -import hudson.Extension; import hudson.PluginWrapper; import hudson.RelativePath; import hudson.XmlFile; @@ -81,9 +80,9 @@ import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.beans.Introspector; import java.util.IdentityHashMap; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; /** * Metadata about a configurable instance. @@ -310,7 +309,7 @@ public abstract class Descriptor> implements Saveable, * Historically some implementations returned null as a way of hiding the descriptor from the UI, * but this is generally managed by an explicit method such as {@code isEnabled} or {@code isApplicable}. */ - @Nonnull + @NonNull public String getDisplayName() { return clazz.getSimpleName(); } @@ -466,7 +465,7 @@ public abstract class Descriptor> implements Saveable, /** * Used by Jelly to abstract away the handling of global.jelly vs config.jelly databinding difference. */ - public @CheckForNull PropertyType getPropertyType(@Nonnull Object instance, @Nonnull String field) { + public @CheckForNull PropertyType getPropertyType(@NonNull Object instance, @NonNull String field) { // in global.jelly, instance==descriptor return instance==this ? getGlobalPropertyType(field) : getPropertyType(field); } @@ -476,7 +475,7 @@ public abstract class Descriptor> implements Saveable, * @throws AssertionError in case the field cannot be found * @since 1.492 */ - public @Nonnull PropertyType getPropertyTypeOrDie(@Nonnull Object instance, @Nonnull String field) { + public @NonNull PropertyType getPropertyTypeOrDie(@NonNull Object instance, @NonNull String field) { PropertyType propertyType = getPropertyType(instance, field); if (propertyType != null) { return propertyType; @@ -571,7 +570,7 @@ public abstract class Descriptor> implements Saveable, * Signals a problem in the submitted form. * @since 1.145 */ - public T newInstance(@Nullable StaplerRequest req, @Nonnull JSONObject formData) throws FormException { + public T newInstance(@Nullable StaplerRequest req, @NonNull JSONObject formData) throws FormException { try { Method m = getClass().getMethod("newInstance", StaplerRequest.class); @@ -828,7 +827,7 @@ public abstract class Descriptor> implements Saveable, * * @since 2.0, used to be in {@link GlobalConfiguration} before that. */ - public @Nonnull GlobalConfigurationCategory getCategory() { + public @NonNull GlobalConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Unclassified.class); } @@ -839,9 +838,9 @@ public abstract class Descriptor> implements Saveable, * Override to return something different if appropriate. The only currently supported alternative return value is {@link Jenkins#MANAGE}. * * @return Permission required to globally configure this descriptor. - * @since TODO + * @since 2.222 */ - public @Nonnull + public @NonNull Permission getRequiredGlobalConfigPagePermission() { return Jenkins.ADMINISTER; } diff --git a/core/src/main/java/hudson/model/DescriptorVisibilityFilter.java b/core/src/main/java/hudson/model/DescriptorVisibilityFilter.java index ef099a64dc..89e8d1b7ba 100644 --- a/core/src/main/java/hudson/model/DescriptorVisibilityFilter.java +++ b/core/src/main/java/hudson/model/DescriptorVisibilityFilter.java @@ -9,8 +9,8 @@ import java.util.WeakHashMap; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.ExtensionFilter; import jenkins.util.SystemProperties; @@ -37,7 +37,7 @@ public abstract class DescriptorVisibilityFilter implements ExtensionPoint { * the descriptor is not shown. * @since 2.12 */ - public boolean filterType(@Nonnull Class contextClass, @Nonnull Descriptor descriptor) { + public boolean filterType(@NonNull Class contextClass, @NonNull Descriptor descriptor) { return true; } @@ -57,7 +57,7 @@ public abstract class DescriptorVisibilityFilter implements ExtensionPoint { * If any of the installed {@link DescriptorVisibilityFilter} returns false, * the descriptor is not shown. */ - public abstract boolean filter(@CheckForNull Object context, @Nonnull Descriptor descriptor); + public abstract boolean filter(@CheckForNull Object context, @NonNull Descriptor descriptor); public static ExtensionList all() { return ExtensionList.lookup(DescriptorVisibilityFilter.class); diff --git a/core/src/main/java/hudson/model/DirectlyModifiableView.java b/core/src/main/java/hudson/model/DirectlyModifiableView.java index b799d2bd9e..e12c34fa11 100644 --- a/core/src/main/java/hudson/model/DirectlyModifiableView.java +++ b/core/src/main/java/hudson/model/DirectlyModifiableView.java @@ -26,7 +26,7 @@ package hudson.model; import java.io.IOException; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import org.kohsuke.stapler.HttpResponse; @@ -48,7 +48,7 @@ public interface DirectlyModifiableView { * @throws IOException Removal failed. * @throws IllegalArgumentException View rejected to remove an item. */ - boolean remove(@Nonnull TopLevelItem item) throws IOException, IllegalArgumentException; + boolean remove(@NonNull TopLevelItem item) throws IOException, IllegalArgumentException; /** * Add item to this view. @@ -56,7 +56,7 @@ public interface DirectlyModifiableView { * @throws IOException Adding failed. * @throws IllegalArgumentException View rejected to add an item. */ - void add(@Nonnull TopLevelItem item) throws IOException, IllegalArgumentException; + void add(@NonNull TopLevelItem item) throws IOException, IllegalArgumentException; /** * Handle addJobToView web method. diff --git a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java index 0f02f1c2a2..b31795eff6 100644 --- a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java +++ b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java @@ -346,13 +346,12 @@ public final class DirectoryBrowserSupport implements HttpResponse { if(LOGGER.isLoggable(Level.FINE)) LOGGER.fine("Serving "+baseFile+" with lastModified=" + lastModified + ", length=" + length); - InputStream in = baseFile.open(); if (view) { // for binary files, provide the file name for download rsp.setHeader("Content-Disposition", "inline; filename=" + baseFile.getName()); // pseudo file name to let the Stapler set text/plain - rsp.serveFile(req, in, lastModified, -1, length, "plain.txt"); + rsp.serveFile(req, baseFile.open(), lastModified, -1, length, "plain.txt"); } else { if (resourceToken != null) { // redirect to second domain @@ -368,7 +367,7 @@ public final class DirectoryBrowserSupport implements HttpResponse { } } } - rsp.serveFile(req, in, lastModified, -1, length, baseFile.getName()); + rsp.serveFile(req, baseFile.open(), lastModified, -1, length, baseFile.getName()); } } } diff --git a/core/src/main/java/hudson/model/EnvironmentContributingAction.java b/core/src/main/java/hudson/model/EnvironmentContributingAction.java index 03ad23fb35..1eed758f72 100644 --- a/core/src/main/java/hudson/model/EnvironmentContributingAction.java +++ b/core/src/main/java/hudson/model/EnvironmentContributingAction.java @@ -31,7 +31,7 @@ import hudson.tasks.BuildWrapper; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.ProtectedExternally; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * {@link Action} that contributes environment variables during a build. @@ -58,7 +58,7 @@ public interface EnvironmentContributingAction extends Action { * Environment variables should be added to this map. * @since 2.76 */ - default void buildEnvironment(@Nonnull Run run, @Nonnull EnvVars env) { + default void buildEnvironment(@NonNull Run run, @NonNull EnvVars env) { if (run instanceof AbstractBuild && Util.isOverridden(EnvironmentContributingAction.class, getClass(), "buildEnvVars", AbstractBuild.class, EnvVars.class)) { diff --git a/core/src/main/java/hudson/model/EnvironmentContributor.java b/core/src/main/java/hudson/model/EnvironmentContributor.java index 591d67cbef..0a4c1ba1ad 100644 --- a/core/src/main/java/hudson/model/EnvironmentContributor.java +++ b/core/src/main/java/hudson/model/EnvironmentContributor.java @@ -30,7 +30,7 @@ import hudson.ExtensionPoint; import hudson.scm.SCM; import java.io.IOException; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Contributes environment variables to builds. @@ -85,7 +85,7 @@ public abstract class EnvironmentContributor implements ExtensionPoint { * @param listener * Connected to the build console. Can be used to report errors. */ - public void buildEnvironmentFor(@Nonnull Run r, @Nonnull EnvVars envs, @Nonnull TaskListener listener) throws IOException, InterruptedException {} + public void buildEnvironmentFor(@NonNull Run r, @NonNull EnvVars envs, @NonNull TaskListener listener) throws IOException, InterruptedException {} /** * Contributes environment variables used for a job. @@ -107,7 +107,7 @@ public abstract class EnvironmentContributor implements ExtensionPoint { * Connected to the build console. Can be used to report errors. * @since 1.527 */ - public void buildEnvironmentFor(@Nonnull Job j, @Nonnull EnvVars envs, @Nonnull TaskListener listener) throws IOException, InterruptedException {} + public void buildEnvironmentFor(@NonNull Job j, @NonNull EnvVars envs, @NonNull TaskListener listener) throws IOException, InterruptedException {} /** * Returns all the registered {@link EnvironmentContributor}s. diff --git a/core/src/main/java/hudson/model/Executor.java b/core/src/main/java/hudson/model/Executor.java index e69fa2100b..2c6e66414b 100644 --- a/core/src/main/java/hudson/model/Executor.java +++ b/core/src/main/java/hudson/model/Executor.java @@ -46,7 +46,7 @@ import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import org.kohsuke.stapler.interceptor.RequirePOST; -import javax.annotation.concurrent.GuardedBy; +import net.jcip.annotations.GuardedBy; import javax.servlet.ServletException; import java.io.IOException; import java.io.PrintWriter; @@ -67,8 +67,8 @@ import hudson.security.ACLContext; import hudson.security.AccessControlled; import java.util.Collection; import static java.util.logging.Level.*; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.queue.AsynchronousExecution; import jenkins.security.QueueItemAuthenticatorConfiguration; import jenkins.security.QueueItemAuthenticatorDescriptor; @@ -85,7 +85,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; */ @ExportedBean public class Executor extends Thread implements ModelObject { - protected final @Nonnull Computer owner; + protected final @NonNull Computer owner; private final Queue queue; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private static final int DEFAULT_ESTIMATED_DURATION = -1; @@ -142,7 +142,7 @@ public class Executor extends Thread implements ModelObject { @GuardedBy("lock") private final List causes = new Vector<>(); - public Executor(@Nonnull Computer owner, int n) { + public Executor(@NonNull Computer owner, int n) { super("Executor #"+n+" for "+owner.getDisplayName()); this.owner = owner; this.queue = Jenkins.get().getQueue(); @@ -534,7 +534,7 @@ public class Executor extends Thread implements ModelObject { * @return Unmodifiable collection of causes of interruption. * @since 1.617 */ - public @Nonnull Collection getCausesOfInterruption() { + public @NonNull Collection getCausesOfInterruption() { return Collections.unmodifiableCollection(causes); } @@ -857,7 +857,7 @@ public class Executor extends Thread implements ModelObject { * @param runExtId * if not null, the externalizable id ({@link Run#getExternalizableId()}) * of the build the user expects to interrupt - * @since TODO + * @since 2.209 */ @RequirePOST @Restricted(NoExternalUse.class) @@ -897,7 +897,7 @@ public class Executor extends Thread implements ModelObject { } } - public @Nonnull Computer getOwner() { + public @NonNull Computer getOwner() { return owner; } diff --git a/core/src/main/java/hudson/model/Failure.java b/core/src/main/java/hudson/model/Failure.java index 4fb00809f0..53f8721311 100644 --- a/core/src/main/java/hudson/model/Failure.java +++ b/core/src/main/java/hudson/model/Failure.java @@ -28,7 +28,7 @@ import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import javax.servlet.ServletException; import java.io.IOException; diff --git a/core/src/main/java/hudson/model/FileParameterValue.java b/core/src/main/java/hudson/model/FileParameterValue.java index 4e059196e6..54d0eee5ee 100644 --- a/core/src/main/java/hudson/model/FileParameterValue.java +++ b/core/src/main/java/hudson/model/FileParameterValue.java @@ -36,9 +36,7 @@ import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.InvalidPathException; -import java.nio.file.Path; import java.util.regex.Pattern; -import javax.servlet.ServletException; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemHeaders; @@ -225,39 +223,11 @@ public class FileParameterValue extends ParameterValue { * * @param request * @param response - * @throws ServletException - * @throws IOException */ - public void doDynamic(StaplerRequest request, StaplerResponse response) throws ServletException, IOException { - if (("/" + originalFileName).equals(request.getRestOfPath())) { - AbstractBuild build = (AbstractBuild)request.findAncestor(AbstractBuild.class).getObject(); - File fileParameter = getLocationUnderBuild(build); - - if (!ALLOW_FOLDER_TRAVERSAL_OUTSIDE_WORKSPACE) { - File fileParameterFolder = getFileParameterFolderUnderBuild(build); - - //TODO can be replaced by Util#isDescendant in 2.80+ - Path child = fileParameter.getAbsoluteFile().toPath().normalize(); - Path parent = fileParameterFolder.getAbsoluteFile().toPath().normalize(); - if (!child.startsWith(parent)) { - throw new IllegalStateException("The fileParameter tried to escape the expected folder: " + location); - } - } - - if (fileParameter.isFile()) { - try (InputStream data = Files.newInputStream(fileParameter.toPath())) { - long lastModified = fileParameter.lastModified(); - long contentLength = fileParameter.length(); - if (request.hasParameter("view")) { - response.serveFile(request, data, lastModified, contentLength, "plain.txt"); - } else { - response.serveFile(request, data, lastModified, contentLength, originalFileName); - } - } catch (InvalidPathException e) { - throw new IOException(e); - } - } - } + public DirectoryBrowserSupport doDynamic(StaplerRequest request, StaplerResponse response) { + AbstractBuild build = (AbstractBuild)request.findAncestor(AbstractBuild.class).getObject(); + File fileParameter = getFileParameterFolderUnderBuild(build); + return new DirectoryBrowserSupport(build, new FilePath(fileParameter), Messages.FileParameterValue_IndexTitle(), "folder.png", false); } /** diff --git a/core/src/main/java/hudson/model/Fingerprint.java b/core/src/main/java/hudson/model/Fingerprint.java index e628c6bd54..e00c857244 100644 --- a/core/src/main/java/hudson/model/Fingerprint.java +++ b/core/src/main/java/hudson/model/Fingerprint.java @@ -71,8 +71,8 @@ import java.util.Map.Entry; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.acegisecurity.AccessDeniedException; import org.acegisecurity.Authentication; import org.xmlpull.v1.XmlPullParserException; @@ -112,7 +112,7 @@ public class Fingerprint implements ModelObject, Saveable { * @return A name of the job */ @Exported - @Nonnull + @NonNull public String getName() { return name; } @@ -159,7 +159,7 @@ public class Fingerprint implements ModelObject, Saveable { * @return A build number */ @Exported - @Nonnull + @NonNull public int getNumber() { return number; } @@ -851,7 +851,7 @@ public class Fingerprint implements ModelObject, Saveable { /** * Time when the fingerprint has been captured. */ - private final @Nonnull Date timestamp; + private final @NonNull Date timestamp; /** * Null if this fingerprint is for a file that's @@ -875,12 +875,12 @@ public class Fingerprint implements ModelObject, Saveable { */ private transient volatile List transientFacets = null; - public Fingerprint(@CheckForNull Run build, @Nonnull String fileName, @Nonnull byte[] md5sum) throws IOException { + public Fingerprint(@CheckForNull Run build, @NonNull String fileName, @NonNull byte[] md5sum) throws IOException { this(build==null ? null : new BuildPtr(build), fileName, md5sum); save(); } - Fingerprint(@CheckForNull BuildPtr original, @Nonnull String fileName, @Nonnull byte[] md5sum) { + Fingerprint(@CheckForNull BuildPtr original, @NonNull String fileName, @NonNull byte[] md5sum) { this.original = original; this.md5sum = md5sum; this.fileName = fileName; @@ -907,7 +907,7 @@ public class Fingerprint implements ModelObject, Saveable { return null; } - public @Nonnull String getDisplayName() { + public @NonNull String getDisplayName() { return fileName; } @@ -915,7 +915,7 @@ public class Fingerprint implements ModelObject, Saveable { * The file name (like "foo.jar" without path). */ @Exported - public @Nonnull String getFileName() { + public @NonNull String getFileName() { return fileName; } @@ -923,7 +923,7 @@ public class Fingerprint implements ModelObject, Saveable { * Gets the MD5 hash string. */ @Exported(name="hash") - public @Nonnull String getHashString() { + public @NonNull String getHashString() { return Util.toHexString(md5sum); } @@ -931,7 +931,7 @@ public class Fingerprint implements ModelObject, Saveable { * Gets the timestamp when this record is created. */ @Exported - public @Nonnull Date getTimestamp() { + public @NonNull Date getTimestamp() { return timestamp; } @@ -941,7 +941,7 @@ public class Fingerprint implements ModelObject, Saveable { * @return * string like "3 minutes" "1 day" etc. */ - public @Nonnull String getTimestampString() { + public @NonNull String getTimestampString() { long duration = System.currentTimeMillis()-timestamp.getTime(); return Util.getTimeSpanString(duration); } @@ -953,7 +953,7 @@ public class Fingerprint implements ModelObject, Saveable { * These builds of this job has used this file. * @return may be empty but not null. */ - public @Nonnull RangeSet getRangeSet(String jobFullName) { + public @NonNull RangeSet getRangeSet(String jobFullName) { RangeSet r = usages.get(jobFullName); if(r==null) r = new RangeSet(); return r; @@ -966,7 +966,7 @@ public class Fingerprint implements ModelObject, Saveable { /** * Gets the sorted list of job names where this jar is used. */ - public @Nonnull List getJobs() { + public @NonNull List getJobs() { List r = new ArrayList<>(usages.keySet()); Collections.sort(r); return r; @@ -991,7 +991,7 @@ public class Fingerprint implements ModelObject, Saveable { // this is for remote API @Exported(name="usage") - public @Nonnull List _getUsages() { + public @NonNull List _getUsages() { List r = new ArrayList<>(); final Jenkins instance = Jenkins.get(); for (Entry e : usages.entrySet()) { @@ -1007,7 +1007,7 @@ public class Fingerprint implements ModelObject, Saveable { * @deprecated Use {@link #addFor(hudson.model.Run)} */ @Deprecated - public synchronized void add(@Nonnull AbstractBuild b) throws IOException { + public synchronized void add(@NonNull AbstractBuild b) throws IOException { addFor(b); } @@ -1016,14 +1016,14 @@ public class Fingerprint implements ModelObject, Saveable { * @param b {@link Run} to be referenced in {@link #usages} * @since 1.577 */ - public synchronized void addFor(@Nonnull Run b) throws IOException { + public synchronized void addFor(@NonNull Run b) throws IOException { add(b.getParent().getFullName(), b.getNumber()); } /** * Records that a build of a job has used this file. */ - public synchronized void add(@Nonnull String jobFullName, int n) throws IOException { + public synchronized void add(@NonNull String jobFullName, int n) throws IOException { addWithoutSaving(jobFullName, n); save(); } @@ -1036,7 +1036,7 @@ public class Fingerprint implements ModelObject, Saveable { return this; } - void addWithoutSaving(@Nonnull String jobFullName, int n) { + void addWithoutSaving(@NonNull String jobFullName, int n) { synchronized(usages) { // TODO why not synchronized (this) like some, though not all, other accesses? RangeSet r = usages.get(jobFullName); if(r==null) { @@ -1155,7 +1155,7 @@ public class Fingerprint implements ModelObject, Saveable { * * @since 1.421 */ - public @Nonnull Collection getFacets() { + public @NonNull Collection getFacets() { if (transientFacets==null) { List transientFacets = new ArrayList<>(); for (TransientFingerprintFacetFactory fff : TransientFingerprintFacetFactory.all()) { @@ -1197,7 +1197,7 @@ public class Fingerprint implements ModelObject, Saveable { * Sorts {@link FingerprintFacet}s by their timestamps. * @return Sorted list of {@link FingerprintFacet}s */ - public @Nonnull Collection getSortedFacets() { + public @NonNull Collection getSortedFacets() { List r = new ArrayList<>(getFacets()); r.sort(new Comparator() { public int compare(FingerprintFacet o1, FingerprintFacet o2) { @@ -1226,7 +1226,7 @@ public class Fingerprint implements ModelObject, Saveable { /** * Returns the actions contributed from {@link #getFacets()} */ - public @Nonnull List getActions() { + public @NonNull List getActions() { List r = new ArrayList<>(); for (FingerprintFacet ff : getFacets()) ff.createActions(r); @@ -1257,8 +1257,7 @@ public class Fingerprint implements ModelObject, Saveable { file.getParentFile().mkdirs(); // JENKINS-16301: fast path for the common case. AtomicFileWriter afw = new AtomicFileWriter(file); - try { - PrintWriter w = new PrintWriter(afw); + try (PrintWriter w = new PrintWriter((afw))) { w.println(""); w.println(""); w.print(" "); @@ -1305,6 +1304,19 @@ public class Fingerprint implements ModelObject, Saveable { } } + /** + * Returns a facet that blocks the deletion of the fingerprint. + * Returns null if no such facet. + * @since 2.223 + */ + public @CheckForNull FingerprintFacet getFacetBlockingDeletion() { + for (FingerprintFacet facet : facets) { + if (facet.isFingerprintDeletionBlocked()) + return facet; + } + return null; + } + /** * Update references to a renamed job in the fingerprint */ @@ -1338,14 +1350,14 @@ public class Fingerprint implements ModelObject, Saveable { /** * The file we save our configuration. */ - private static @Nonnull XmlFile getConfigFile(@Nonnull File file) { + private static @NonNull XmlFile getConfigFile(@NonNull File file) { return new XmlFile(XSTREAM,file); } /** * Determines the file name from md5sum. */ - private static @Nonnull File getFingerprintFile(@Nonnull byte[] md5sum) { + private static @NonNull File getFingerprintFile(@NonNull byte[] md5sum) { assert md5sum.length==16; return new File( Jenkins.get().getRootDir(), "fingerprints/"+ Util.toHexString(md5sum,0,1)+'/'+Util.toHexString(md5sum,1,1)+'/'+Util.toHexString(md5sum,2,md5sum.length-2)+".xml"); @@ -1356,10 +1368,10 @@ public class Fingerprint implements ModelObject, Saveable { * @return Loaded {@link Fingerprint}. Null if the config file does not exist or * malformed. */ - /*package*/ static @CheckForNull Fingerprint load(@Nonnull byte[] md5sum) throws IOException { + /*package*/ static @CheckForNull Fingerprint load(@NonNull byte[] md5sum) throws IOException { return load(getFingerprintFile(md5sum)); } - /*package*/ static @CheckForNull Fingerprint load(@Nonnull File file) throws IOException { + /*package*/ static @CheckForNull Fingerprint load(@NonNull File file) throws IOException { XmlFile configFile = getConfigFile(file); if(!configFile.exists()) return null; @@ -1426,7 +1438,7 @@ public class Fingerprint implements ModelObject, Saveable { * @param fullName Full name of the job * @return {@code true} if the user can discover the item */ - private static boolean canDiscoverItem(@Nonnull final String fullName) { + private static boolean canDiscoverItem(@NonNull final String fullName) { final Jenkins jenkins = Jenkins.get(); // Fast check to avoid security context switches @@ -1478,7 +1490,7 @@ public class Fingerprint implements ModelObject, Saveable { * @return the XStream instance * @since 1.655 */ - @Nonnull + @NonNull public static XStream2 getXStream() { return XSTREAM; } diff --git a/core/src/main/java/hudson/model/FingerprintCleanupThread.java b/core/src/main/java/hudson/model/FingerprintCleanupThread.java index 292248ed52..42631e8b5d 100644 --- a/core/src/main/java/hudson/model/FingerprintCleanupThread.java +++ b/core/src/main/java/hudson/model/FingerprintCleanupThread.java @@ -26,6 +26,7 @@ package hudson.model; import hudson.Extension; import hudson.ExtensionList; import hudson.Functions; +import jenkins.model.FingerprintFacet; import jenkins.model.Jenkins; import org.jenkinsci.Symbol; import org.kohsuke.accmod.Restricted; @@ -33,6 +34,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; import java.io.File; import java.io.IOException; +import java.util.Date; import java.util.regex.Pattern; /** @@ -107,11 +109,15 @@ public class FingerprintCleanupThread extends AsyncPeriodicWork { private boolean check(File fingerprintFile, TaskListener listener) { try { Fingerprint fp = loadFingerprint(fingerprintFile); - if (fp == null || !fp.isAlive()) { + if (fp == null || (!fp.isAlive() && fp.getFacetBlockingDeletion() == null) ) { listener.getLogger().println("deleting obsolete " + fingerprintFile); fingerprintFile.delete(); return true; } else { + if (!fp.isAlive()) { + FingerprintFacet deletionBlockerFacet = fp.getFacetBlockingDeletion(); + listener.getLogger().println(deletionBlockerFacet.getClass().getName() + " created on " + new Date(deletionBlockerFacet.getTimestamp()) + " blocked deletion of " + fingerprintFile); + } // get the fingerprint in the official map so have the changes visible to Jenkins // otherwise the mutation made in FingerprintMap can override our trimming. fp = getFingerprint(fp); diff --git a/core/src/main/java/hudson/model/FingerprintMap.java b/core/src/main/java/hudson/model/FingerprintMap.java index 2727d22174..16e9bd7241 100644 --- a/core/src/main/java/hudson/model/FingerprintMap.java +++ b/core/src/main/java/hudson/model/FingerprintMap.java @@ -30,8 +30,8 @@ import jenkins.model.Jenkins; import java.io.File; import java.io.IOException; import java.util.Locale; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Cache of {@link Fingerprint}s. @@ -60,15 +60,15 @@ public final class FingerprintMap extends KeyedDataStorage extends PersistenceRoot, ModelObject /** * Gets all the items in this collection in a read-only view * that matches supplied Predicate - * @since TODO + * @since 2.221 */ default Collection getItems(Predicate pred) { return getItemsStream(pred) @@ -71,7 +70,7 @@ public interface ItemGroup extends PersistenceRoot, ModelObject /** * Gets a read-only stream of all the items in this collection - * @since TODO + * @since 2.221 */ default Stream getItemsStream() { return getItems().stream(); @@ -80,7 +79,7 @@ public interface ItemGroup extends PersistenceRoot, ModelObject /** * Gets a read-only stream of all the items in this collection * that matches supplied Predicate - * @since TODO + * @since 2.221 */ default Stream getItemsStream(Predicate pred) { return getItemsStream().filter(pred); @@ -132,7 +131,7 @@ public interface ItemGroup extends PersistenceRoot, ModelObject /** * Similar to {@link #getAllItems(Class)} with additional predicate filtering - * @since TODO + * @since 2.221 */ default List getAllItems(Class type, Predicate pred) { return Items.getAllItems(this, type, pred); @@ -150,7 +149,7 @@ public interface ItemGroup extends PersistenceRoot, ModelObject /** * Gets all the {@link Item}s unordered, lazily and recursively in the {@link ItemGroup} tree * and filter them by the given type and given predicate - * @since TODO + * @since 2.221 */ default Iterable allItems(Class type, Predicate pred) { return Items.allItems(this, type, pred); diff --git a/core/src/main/java/hudson/model/Items.java b/core/src/main/java/hudson/model/Items.java index 8c31c0bf1d..298d302358 100644 --- a/core/src/main/java/hudson/model/Items.java +++ b/core/src/main/java/hudson/model/Items.java @@ -48,8 +48,8 @@ import java.util.NoSuchElementException; import java.util.Stack; import java.util.StringTokenizer; import java.util.function.Predicate; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.DirectlyModifiableTopLevelItemGroup; import jenkins.model.Jenkins; import org.acegisecurity.Authentication; @@ -222,7 +222,7 @@ public class Items { /** * Does the opposite of {@link #toNameList(Collection)}. */ - public static List fromNameList(ItemGroup context, @Nonnull String list, @Nonnull Class type) { + public static List fromNameList(ItemGroup context, @NonNull String list, @NonNull Class type) { final Jenkins jenkins = Jenkins.get(); List r = new ArrayList<>(); @@ -413,7 +413,7 @@ public class Items { * @param pred Predicate condition to filter items * @return List of items matching given criteria * - * @since TODO + * @since 2.221 */ public static List getAllItems(final ItemGroup root, Class type, Predicate pred) { List r = new ArrayList<>(); @@ -421,7 +421,7 @@ public class Items { return r; } private static void getAllItems(final ItemGroup root, Class type, List r, Predicate pred) { - List items = new ArrayList<>(((ItemGroup) root).getItems()); + List items = new ArrayList<>(((ItemGroup) root).getItems(t -> t instanceof ItemGroup || (type.isInstance(t) && pred.test(type.cast(t))))); // because we add items depth first, we can use the quicker BY_NAME comparison items.sort(BY_NAME); for (Item i : items) { @@ -466,7 +466,7 @@ public class Items { * @param the type. * @param the predicate. * @return An {@link Iterable} for all items. - * @since TODO + * @since 2.221 */ public static Iterable allItems(ItemGroup root, Class type, Predicate pred) { return allItems(Jenkins.getAuthentication(), root, type, pred); @@ -502,7 +502,7 @@ public class Items { * @param the type. * @param pred the predicate. * @return An {@link Iterable} for all items. - * @since TODO + * @since 2.221 */ public static Iterable allItems(Authentication authentication, ItemGroup root, Class type, Predicate pred) { return new AllItemsIterable<>(root, authentication, type, pred); @@ -677,7 +677,7 @@ public class Items { * @throws IllegalArgumentException if there is already something there, which you were supposed to know about * @throws Failure if there is already something there but you should not be told details */ - static void verifyItemDoesNotAlreadyExist(@Nonnull ItemGroup parent, @Nonnull String newName, @CheckForNull Item variant) throws IllegalArgumentException, Failure { + static void verifyItemDoesNotAlreadyExist(@NonNull ItemGroup parent, @NonNull String newName, @CheckForNull Item variant) throws IllegalArgumentException, Failure { Item existing; try (ACLContext ctxt = ACL.as(ACL.SYSTEM)) { existing = parent.getItem(newName); diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java index caddc5ca60..4f695371f2 100644 --- a/core/src/main/java/hudson/model/Job.java +++ b/core/src/main/java/hudson/model/Job.java @@ -77,8 +77,8 @@ import java.util.Map; import java.util.SortedMap; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import jenkins.model.BuildDiscarder; import jenkins.model.BuildDiscarderProperty; @@ -375,7 +375,7 @@ public abstract class Job, RunT extends Run> implements ReconfigurableD * @see ProminentProjectAction * @see PermalinkProjectAction */ - @Nonnull + @NonNull public Collection getJobActions(J job) { // delegate to getJobAction (singular) for backward compatible behavior Action a = getJobAction(job); @@ -170,7 +170,7 @@ public abstract class JobProperty> implements ReconfigurableD return getJobAction((J)project); } - @Nonnull + @NonNull public final Collection getProjectActions(AbstractProject project) { return getJobActions((J)project); } diff --git a/core/src/main/java/hudson/model/Label.java b/core/src/main/java/hudson/model/Label.java index f9ce39340d..537efcabe0 100644 --- a/core/src/main/java/hudson/model/Label.java +++ b/core/src/main/java/hudson/model/Label.java @@ -26,6 +26,7 @@ package hudson.model; import antlr.ANTLRException; import static hudson.Util.fixNull; +import hudson.Util; import hudson.model.labels.LabelAtom; import hudson.model.labels.LabelExpression; import hudson.model.labels.LabelExpression.And; @@ -57,7 +58,6 @@ import org.kohsuke.stapler.export.ExportedBean; import java.io.Serializable; import java.io.StringReader; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -74,7 +74,7 @@ import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.HierarchicalStreamReader; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Group of {@link Node}s. @@ -88,19 +88,19 @@ public abstract class Label extends Actionable implements Comparable

* This status code is used in a multi-stage build (like maven2) * where a problem in earlier stage prevented later stages from building. */ - public static final @Nonnull Result NOT_BUILT = new Result("NOT_BUILT",BallColor.NOTBUILT,3,false); + public static final @NonNull Result NOT_BUILT = new Result("NOT_BUILT",BallColor.NOTBUILT,3,false); /** * The build was manually aborted. * * If you are catching {@link InterruptedException} and interpreting it as {@link #ABORTED}, * you should check {@link Executor#abortResult()} instead (starting 1.417.) */ - public static final @Nonnull Result ABORTED = new Result("ABORTED",BallColor.ABORTED,4,false); + public static final @NonNull Result ABORTED = new Result("ABORTED",BallColor.ABORTED,4,false); - private final @Nonnull String name; + private final @NonNull String name; /** * Bigger numbers are worse. */ - public final @Nonnegative int ordinal; + public final /* @java.annotation.Nonnegative */ int ordinal; /** * Default ball color for this status. */ - public final @Nonnull BallColor color; + public final @NonNull BallColor color; /** * Is this a complete build - i.e. did it run to the end (not aborted)? @@ -97,7 +96,7 @@ public final class Result implements Serializable, CustomExportedBean { */ public final boolean completeBuild; - private Result(@Nonnull String name, @Nonnull BallColor color, @Nonnegative int ordinal, boolean complete) { + private Result(@NonNull String name, @NonNull BallColor color, /*@java.annotation.Nonnegative */int ordinal, boolean complete) { this.name = name; this.color = color; this.ordinal = ordinal; @@ -107,26 +106,26 @@ public final class Result implements Serializable, CustomExportedBean { /** * Combines two {@link Result}s and returns the worse one. */ - public @Nonnull Result combine(@Nonnull Result that) { + public @NonNull Result combine(@NonNull Result that) { if(this.ordinal < that.ordinal) return that; else return this; } - public boolean isWorseThan(@Nonnull Result that) { + public boolean isWorseThan(@NonNull Result that) { return this.ordinal > that.ordinal; } - public boolean isWorseOrEqualTo(@Nonnull Result that) { + public boolean isWorseOrEqualTo(@NonNull Result that) { return this.ordinal >= that.ordinal; } - public boolean isBetterThan(@Nonnull Result that) { + public boolean isBetterThan(@NonNull Result that) { return this.ordinal < that.ordinal; } - public boolean isBetterOrEqualTo(@Nonnull Result that) { + public boolean isBetterOrEqualTo(@NonNull Result that) { return this.ordinal <= that.ordinal; } @@ -139,22 +138,22 @@ public final class Result implements Serializable, CustomExportedBean { } @Override - public @Nonnull String toString() { + public @NonNull String toString() { return name; } - public @Nonnull String toExportedObject() { + public @NonNull String toExportedObject() { return name; } - public static @Nonnull Result fromString(@Nonnull String s) { + public static @NonNull Result fromString(@NonNull String s) { for (Result r : all) if (s.equalsIgnoreCase(r.name)) return r; return FAILURE; } - private static @Nonnull List getNames() { + private static @NonNull List getNames() { List l = new ArrayList<>(); for (Result r : all) l.add(r.name); diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java index 22bac37e1f..6943e765e6 100644 --- a/core/src/main/java/hudson/model/Run.java +++ b/core/src/main/java/hudson/model/Run.java @@ -100,8 +100,8 @@ import java.util.logging.Level; import static java.util.logging.Level.*; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; import jenkins.model.ArtifactManager; @@ -157,7 +157,16 @@ public abstract class Run ,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run getBadgeActions() { + public @NonNull List getBadgeActions() { List r = getActions(BuildBadgeAction.class); if(isKeepLog()) { r = new ArrayList<>(r); @@ -559,7 +572,7 @@ public abstract class Run ,RunT extends Run,RunT extends Run getCauses() { + public @NonNull List getCauses() { CauseAction a = getAction(CauseAction.class); if (a==null) return Collections.emptyList(); return Collections.unmodifiableList(a.getCauses()); @@ -616,7 +629,7 @@ public abstract class Run ,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run') { @@ -702,7 +726,7 @@ public abstract class Run ,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run createReference() { + protected @NonNull BuildReference createReference() { return new BuildReference<>(getId(), _this()); } @@ -952,7 +976,7 @@ public abstract class Run ,RunT extends Run getPreviousBuildsOverThreshold(int numberOfBuilds, @Nonnull Result threshold) { + public @NonNull List getPreviousBuildsOverThreshold(int numberOfBuilds, @NonNull Result threshold) { RunT r = getPreviousBuild(); if (r != null) { return r.getBuildsOverThreshold(numberOfBuilds, threshold); @@ -968,9 +992,9 @@ public abstract class Run ,RunT extends Run getBuildsOverThreshold(int numberOfBuilds, @Nonnull Result threshold) { + protected @NonNull List getBuildsOverThreshold(int numberOfBuilds, @NonNull Result threshold) { List builds = new ArrayList<>(numberOfBuilds); RunT r = _this(); @@ -1000,7 +1024,7 @@ public abstract class Run ,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run getArtifacts() { + public @NonNull List getArtifacts() { return getArtifactsUpTo(Integer.MAX_VALUE); } @@ -1118,7 +1142,7 @@ public abstract class Run ,RunT extends Run getArtifactsUpTo(int artifactsNumber) { + public @NonNull List getArtifactsUpTo(int artifactsNumber) { SerializableArtifactList sal; VirtualFile root = getArtifactManager().root(); try { @@ -1158,9 +1182,9 @@ public abstract class Run ,RunT extends Run,RunT extends Run,RunT extends Run getBuildFingerprints() { FingerprintAction fingerprintAction = getAction(FingerprintAction.class); @@ -1455,7 +1479,7 @@ public abstract class Run ,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run * The method does not close the {@link OutputStream}. */ - public void writeWholeLogTo(@Nonnull OutputStream out) throws IOException, InterruptedException { + public void writeWholeLogTo(@NonNull OutputStream out) throws IOException, InterruptedException { long pos = 0; AnnotatedLargeText logText; logText = getLogText(); @@ -1539,12 +1563,12 @@ public abstract class Run ,RunT extends Run,RunT extends Run,RunT extends Run.RunExecution exec = RunnerStack.INSTANCE.peek(); if (exec == null) { return; @@ -1658,7 +1682,7 @@ public abstract class Run ,RunT extends Run.RunExecution exec = RunnerStack.INSTANCE.peek(); if (exec == null) { @@ -1704,12 +1728,12 @@ public abstract class Run ,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run getAttributes() { + public @NonNull Map getAttributes() { return attributes; } } @@ -1806,11 +1830,11 @@ public abstract class Run ,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run getLog(int maxLines) throws IOException { + public @NonNull List getLog(int maxLines) throws IOException { if (maxLines == 0) { return Collections.emptyList(); } @@ -2120,7 +2144,7 @@ public abstract class Run ,RunT extends Run,RunT extends Run run, @Nonnull ResultTrend trend); + public abstract @CheckForNull Summary summarize(@NonNull Run run, @NonNull ResultTrend trend); } /** @@ -2160,7 +2184,7 @@ public abstract class Run ,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run,RunT extends Run ORDER_BY_DATE = new Comparator() { - public int compare(@Nonnull Run lhs, @Nonnull Run rhs) { + public int compare(@NonNull Run lhs, @NonNull Run rhs) { long lt = lhs.getTimeInMillis(); long rt = rhs.getTimeInMillis(); return Long.compare(rt, lt); diff --git a/core/src/main/java/hudson/model/RunParameterValue.java b/core/src/main/java/hudson/model/RunParameterValue.java index 71bc170126..39e152666d 100644 --- a/core/src/main/java/hudson/model/RunParameterValue.java +++ b/core/src/main/java/hudson/model/RunParameterValue.java @@ -28,7 +28,7 @@ import jenkins.model.Jenkins; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.export.Exported; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import java.util.Locale; public class RunParameterValue extends ParameterValue { diff --git a/core/src/main/java/hudson/model/RunnerStack.java b/core/src/main/java/hudson/model/RunnerStack.java index 8065ce9cac..0e93b53168 100644 --- a/core/src/main/java/hudson/model/RunnerStack.java +++ b/core/src/main/java/hudson/model/RunnerStack.java @@ -28,7 +28,7 @@ import hudson.model.Run.RunExecution; import java.util.Stack; import java.util.Map; import java.util.WeakHashMap; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Keeps track of {@link RunExecution}s that are currently executing on the given thread diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java index 32db580e1b..388b6c2c55 100644 --- a/core/src/main/java/hudson/model/Slave.java +++ b/core/src/main/java/hudson/model/Slave.java @@ -64,8 +64,8 @@ import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import jenkins.model.Jenkins; import jenkins.security.MasterToSlaveCallable; @@ -126,7 +126,7 @@ public abstract class Slave extends Node implements Serializable { /** * Number of executors of this node. */ - private int numExecutors = 2; + private int numExecutors = 1; /** * Job allocation strategy. @@ -151,11 +151,6 @@ public abstract class Slave extends Node implements Serializable { private /*almost final*/ DescribableList,NodePropertyDescriptor> nodeProperties = new DescribableList<>(this); - /** - * Lazily computed set of labels from {@link #label}. - */ - private transient volatile Set

*/ @Exported - @Nonnull + @NonNull public final String id; /** @@ -818,14 +832,14 @@ public class UpdateSite { * */ @Exported - @Nonnull + @NonNull public final String component; /** * A short, English language explanation for this warning. */ @Exported - @Nonnull + @NonNull public final String message; /** @@ -833,7 +847,7 @@ public class UpdateSite { * only, so */ @Exported - @Nonnull + @NonNull public final String url; /** @@ -842,7 +856,7 @@ public class UpdateSite { * If this list is empty, all versions of the component are considered to be affected by this warning. */ @Exported - @Nonnull + @NonNull public final List versionRanges; /** @@ -896,7 +910,7 @@ public class UpdateSite { return id.hashCode(); } - public boolean isPluginWarning(@Nonnull String pluginName) { + public boolean isPluginWarning(@NonNull String pluginName) { return type == Type.PLUGIN && pluginName.equals(this.component); } @@ -933,7 +947,7 @@ public class UpdateSite { } } - public boolean isRelevantToVersion(@Nonnull VersionNumber version) { + public boolean isRelevantToVersion(@NonNull VersionNumber version) { if (this.versionRanges.isEmpty()) { // no version ranges specified, so all versions are affected return true; @@ -1019,6 +1033,21 @@ public class UpdateSite { */ private Set incompatibleParentPlugins; + /** + * Date when this plugin was released. + * @since 2.224 + */ + @Exported + public final Date releaseTimestamp; + + /** + * Popularity of this plugin. + * + * @since TODO + */ + @Restricted(NoExternalUse.class) + public final Double popularity; + @DataBoundConstructor public Plugin(String sourceId, JSONObject o) { super(sourceId, o, UpdateSite.this.url); @@ -1028,6 +1057,26 @@ public class UpdateSite { this.compatibleSinceVersion = Util.intern(get(o,"compatibleSinceVersion")); this.minimumJavaVersion = Util.intern(get(o, "minimumJavaVersion")); this.requiredCore = Util.intern(get(o,"requiredCore")); + final String releaseTimestamp = get(o, "releaseTimestamp"); + Date date = null; + if (releaseTimestamp != null) { + try { + date = Date.from(Instant.parse(releaseTimestamp)); + } catch (Exception ex) { + LOGGER.log(Level.FINE, "Failed to parse releaseTimestamp for " + title + " from " + sourceId, ex); + } + } + final String popularityFromJson = get(o, "popularity"); + Double popularity = 0.0; + if (popularityFromJson != null) { + try { + popularity = Double.parseDouble(popularityFromJson); + } catch (NumberFormatException nfe) { + LOGGER.log(Level.FINE, "Failed to parse popularity: '" + popularityFromJson + "' for plugin " + this.title); + } + } + this.popularity = popularity; + this.releaseTimestamp = date; this.categories = o.has("labels") ? internInPlace((String[])o.getJSONArray("labels").toArray(EMPTY_STRING_ARRAY)) : null; JSONArray ja = o.getJSONArray("dependencies"); int depCount = (int)(ja.stream().filter(IS_DEP_PREDICATE.and(IS_NOT_OPTIONAL)).count()); @@ -1277,10 +1326,36 @@ public class UpdateSite { return getDependenciesIncompatibleWithInstalledVersion(cache).isEmpty(); } + /** + * Returns true if and only if this update addressed a currently active security vulnerability. + * + * @return true if and only if this update addressed a currently active security vulnerability. + */ + @Restricted(NoExternalUse.class) // Jelly + public boolean fixesSecurityVulnerabilities() { + final PluginWrapper installed = getInstalled(); + if (installed == null) { + return false; + } + boolean allWarningsStillApply = true; + for (Warning warning : ExtensionList.lookupSingleton(UpdateSiteWarningsMonitor.class).getActivePluginWarningsByPlugin().getOrDefault(installed, Collections.emptyList())) { + boolean thisWarningApplies = false; + for (WarningVersionRange range : warning.versionRanges) { + if (range.includes(new VersionNumber(version))) { + thisWarningApplies = true; + } + } + if (!thisWarningApplies) { + allWarningsStillApply = false; + } + } + return !allWarningsStillApply; + } + /** * Get the list of incompatible dependencies (if there are any, as determined by isNeededDependenciesCompatibleWithInstalledVersion) * - * @since TODO + * @since 2.203 */ @Restricted(NoExternalUse.class) // table.jelly @SuppressWarnings("unchecked") diff --git a/core/src/main/java/hudson/model/UsageStatistics.java b/core/src/main/java/hudson/model/UsageStatistics.java index beac77113f..06b2afb8fe 100644 --- a/core/src/main/java/hudson/model/UsageStatistics.java +++ b/core/src/main/java/hudson/model/UsageStatistics.java @@ -27,6 +27,7 @@ import hudson.PluginWrapper; import hudson.Util; import hudson.Extension; import hudson.node_monitors.ArchitectureMonitor.DescriptorImpl; +import hudson.security.Permission; import hudson.util.Secret; import static java.util.concurrent.TimeUnit.DAYS; @@ -35,6 +36,7 @@ import net.sf.json.JSONObject; import org.apache.commons.io.output.ByteArrayOutputStream; import org.kohsuke.stapler.StaplerRequest; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.crypto.Cipher; import javax.crypto.CipherOutputStream; import javax.crypto.KeyGenerator; @@ -192,6 +194,12 @@ public class UsageStatistics extends PageDecorator implements PersistentDescript } } + @NonNull + @Override + public Permission getRequiredGlobalConfigPagePermission() { + return Jenkins.MANAGE; + } + @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { try { diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java index 86f9c9e8c4..9d46dc7ee6 100644 --- a/core/src/main/java/hudson/model/User.java +++ b/core/src/main/java/hudson/model/User.java @@ -62,9 +62,9 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; @@ -243,7 +243,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * @return the {@link jenkins.model.IdStrategy} for use with {@link User} instances. * @since 1.566 */ - @Nonnull + @NonNull public static IdStrategy idStrategy() { Jenkins j = Jenkins.get(); SecurityRealm realm = j.getSecurityRealm(); @@ -253,7 +253,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr return realm.getUserIdStrategy(); } - public int compareTo(@Nonnull User that) { + public int compareTo(@NonNull User that) { return idStrategy().compare(this.id, that.id); } @@ -262,11 +262,11 @@ public class User extends AbstractModelObject implements AccessControlled, Descr return id; } - public @Nonnull String getUrl() { + public @NonNull String getUrl() { return "user/" + Util.rawEncode(idStrategy().keyFor(id)); } - public @Nonnull String getSearchUrl() { + public @NonNull String getSearchUrl() { return "/user/" + Util.rawEncode(idStrategy().keyFor(id)); } @@ -274,7 +274,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * The URL of the user page. */ @Exported(visibility = 999) - public @Nonnull String getAbsoluteUrl() { + public @NonNull String getAbsoluteUrl() { return Jenkins.get().getRootUrl() + getUrl(); } @@ -283,7 +283,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * This is configurable by the user. */ @Exported(visibility = 999) - public @Nonnull String getFullName() { + public @NonNull String getFullName() { return fullName; } @@ -320,7 +320,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr /** * Updates the user object by adding a property. */ - public synchronized void addProperty(@Nonnull UserProperty p) throws IOException { + public synchronized void addProperty(@NonNull UserProperty p) throws IOException { UserProperty old = getProperty(p.getClass()); List ps = new ArrayList<>(properties); if (old != null) @@ -365,7 +365,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * @throws UsernameNotFoundException If this user is not a valid user in the backend {@link SecurityRealm}. * @since 1.419 */ - public @Nonnull Authentication impersonate() throws UsernameNotFoundException { + public @NonNull Authentication impersonate() throws UsernameNotFoundException { return this.impersonate(this.getUserDetailsForImpersonation()); } @@ -378,7 +378,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * @return userDetails for the user, in case he's not found but seems legitimate, we provide a userDetails with minimum access * @throws UsernameNotFoundException If this user is not a valid user in the backend {@link SecurityRealm}. */ - public @Nonnull UserDetails getUserDetailsForImpersonation() throws UsernameNotFoundException { + public @NonNull UserDetails getUserDetailsForImpersonation() throws UsernameNotFoundException { ImpersonatingUserDetailsService userDetailsService = new ImpersonatingUserDetailsService( Jenkins.get().getSecurityRealm().getSecurityComponents().userDetails ); @@ -424,7 +424,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * @see #getUserDetailsForImpersonation() */ @Restricted(NoExternalUse.class) - public @Nonnull Authentication impersonate(@Nonnull UserDetails userDetails) { + public @NonNull Authentication impersonate(@NonNull UserDetails userDetails) { return new UsernamePasswordAuthenticationToken(userDetails.getUsername(), "", userDetails.getAuthorities()); } @@ -446,7 +446,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr *

* This is used to avoid null {@link User} instance. */ - public static @Nonnull User getUnknown() { + public static @NonNull User getUnknown() { return getById(UNKNOWN_USERNAME, true); } @@ -482,7 +482,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * @return An existing or created user. May be {@code null} if a user does not exist and * {@code create} is false. */ - public static @Nullable User get(String idOrFullName, boolean create, @Nonnull Map context) { + public static @Nullable User get(String idOrFullName, boolean create, @NonNull Map context) { if (idOrFullName == null) { return null; } @@ -502,7 +502,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * @return An existing or created user. May be {@code null} if a user does not exist and * {@code create} is false. */ - private static @Nullable User getOrCreateById(@Nonnull String id, @Nonnull String fullName, boolean create) { + private static @Nullable User getOrCreateById(@NonNull String id, @NonNull String fullName, boolean create) { User u = AllUsers.get(id); if (u == null && (create || UserIdMapper.getInstance().isMapped(id))) { u = new User(id, fullName); @@ -535,7 +535,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * Otherwise use {@link #getOrCreateByIdOrFullName(String)} or {@link #get(String, boolean, Map)}. */ @Deprecated - public static @Nonnull User get(String idOrFullName) { + public static @NonNull User get(String idOrFullName) { return getOrCreateByIdOrFullName(idOrFullName); } @@ -554,7 +554,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * @return User instance. It will be created on-demand. * @since 2.91 */ - public static @Nonnull User getOrCreateByIdOrFullName(@Nonnull String idOrFullName) { + public static @NonNull User getOrCreateByIdOrFullName(@NonNull String idOrFullName) { return get(idOrFullName, true, Collections.emptyMap()); } @@ -603,7 +603,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr /** * Gets all the users. */ - public static @Nonnull Collection getAll() { + public static @NonNull Collection getAll() { final IdStrategy strategy = idStrategy(); ArrayList users = new ArrayList<>(AllUsers.values()); users.sort((o1, o2) -> strategy.compare(o1.getId(), o2.getId())); @@ -641,14 +641,14 @@ public class User extends AbstractModelObject implements AccessControlled, Descr /** * Returns the user name. */ - public @Nonnull String getDisplayName() { + public @NonNull String getDisplayName() { return getFullName(); } /** * true if {@link AbstractBuild#hasParticipant} or {@link hudson.model.Cause.UserIdCause} */ - private boolean relatedTo(@Nonnull AbstractBuild b) { + private boolean relatedTo(@NonNull AbstractBuild b) { if (b.hasParticipant(this)) { return true; } @@ -669,7 +669,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr */ @SuppressWarnings("unchecked") @WithBridgeMethods(List.class) - public @Nonnull RunList getBuilds() { + public @NonNull RunList getBuilds() { return RunList.fromJobs((Iterable) Jenkins.get(). allItems(Job.class)).filter((Predicate>) r -> r instanceof AbstractBuild && relatedTo((AbstractBuild) r)); } @@ -679,7 +679,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * * @since 1.191 */ - public @Nonnull Set> getProjects() { + public @NonNull Set> getProjects() { Set> r = new HashSet<>(); for (AbstractProject p : Jenkins.get().allItems(AbstractProject.class)) if (p.hasParticipant(this)) @@ -895,7 +895,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr } @Override - @Nonnull + @NonNull public ACL getACL() { ACL base = Jenkins.get().getAuthorizationStrategy().getACL(this); // always allow a non-anonymous user full control of himself. @@ -920,7 +920,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * @return a possibly empty list * @since 1.498 */ - public @Nonnull List getAuthorities() { + public @NonNull List getAuthorities() { if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) { return Collections.emptyList(); } @@ -1101,7 +1101,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr public static final String REALM = "realm"; @Override - public int compareTo(@Nonnull CanonicalIdResolver o) { + public int compareTo(@NonNull CanonicalIdResolver o) { // reverse priority order return Integer.compare(o.getPriority(), getPriority()); } @@ -1148,7 +1148,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * @since 2.93 */ @CheckForNull - public static String resolve(@Nonnull String idOrFullName, @Nonnull Map context) { + public static String resolve(@NonNull String idOrFullName, @NonNull Map context) { for (CanonicalIdResolver resolver : CanonicalIdResolver.all()) { String id = resolver.resolveCanonicalId(idOrFullName, context); if (id != null) { diff --git a/core/src/main/java/hudson/model/UserIdMapper.java b/core/src/main/java/hudson/model/UserIdMapper.java index d3cd8c0342..4858fdb8df 100644 --- a/core/src/main/java/hudson/model/UserIdMapper.java +++ b/core/src/main/java/hudson/model/UserIdMapper.java @@ -34,7 +34,7 @@ import jenkins.model.IdStrategy; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import java.io.File; import java.io.IOException; import java.nio.file.Files; diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index 190eb985f9..fde5d094c4 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -54,7 +54,7 @@ import hudson.util.RunList; import hudson.util.XStream2; import hudson.views.ListViewColumn; import hudson.widgets.Widget; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; import jenkins.model.ModelObjectWithChildren; import jenkins.model.ModelObjectWithContextMenu; @@ -193,7 +193,7 @@ public abstract class View extends AbstractModelObject implements AccessControll /** * Gets all the items in this collection in a read-only view. */ - @Nonnull + @NonNull @Exported(name="jobs") public abstract Collection getItems(); @@ -244,7 +244,7 @@ public abstract class View extends AbstractModelObject implements AccessControll * @see #rename(String) */ @Exported(visibility=2,name="name") - @Nonnull + @NonNull public String getViewName() { return name; } @@ -384,12 +384,15 @@ public abstract class View extends AbstractModelObject implements AccessControll } /** - * Enables or disables automatic refreshes of the view. - * By default, automatic refreshes are enabled. + * Used to enable or disable automatic refreshes of the view. + * * @since 1.557 + * + * @deprecated Auto-refresh has been removed */ + @Deprecated public boolean isAutomaticRefreshEnabled() { - return true; + return false; } /** @@ -1275,7 +1278,7 @@ public abstract class View extends AbstractModelObject implements AccessControll * NOTE: Historically this method is only ever called from a {@link StaplerRequest} * @return the list of instantiable {@link ViewDescriptor} instances for the current {@link StaplerRequest} */ - @Nonnull + @NonNull public static List allInstantiable() { List r = new ArrayList<>(); StaplerRequest request = Stapler.getCurrentRequest(); diff --git a/core/src/main/java/hudson/model/ViewDescriptor.java b/core/src/main/java/hudson/model/ViewDescriptor.java index 7e4a38f0f2..35481d30ff 100644 --- a/core/src/main/java/hudson/model/ViewDescriptor.java +++ b/core/src/main/java/hudson/model/ViewDescriptor.java @@ -30,8 +30,8 @@ import hudson.views.ViewJobFilter; import java.util.Iterator; import java.util.List; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.DirectlyModifiableTopLevelItemGroup; import jenkins.model.Jenkins; import org.apache.commons.lang.StringUtils; @@ -138,7 +138,7 @@ public abstract class ViewDescriptor extends Descriptor { * @since 2.37 */ @SuppressWarnings("unused") // expose utility check method to subclasses - protected FormValidation checkDisplayName(@Nonnull View view, @CheckForNull String value) { + protected FormValidation checkDisplayName(@NonNull View view, @CheckForNull String value) { if (StringUtils.isBlank(value)) { // no custom name, no need to check return FormValidation.ok(); diff --git a/core/src/main/java/hudson/model/ViewGroup.java b/core/src/main/java/hudson/model/ViewGroup.java index 628667157a..b90037a568 100644 --- a/core/src/main/java/hudson/model/ViewGroup.java +++ b/core/src/main/java/hudson/model/ViewGroup.java @@ -33,7 +33,7 @@ import java.util.LinkedHashSet; import java.util.List; import jenkins.model.Jenkins; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Container of {@link View}s. @@ -69,7 +69,7 @@ public interface ViewGroup extends Saveable, ModelObject, AccessControlled { * * @since 2.174 */ - @Nonnull + @NonNull default Collection getAllViews() { final Collection views = new LinkedHashSet<>(getViews()); diff --git a/core/src/main/java/hudson/model/ViewGroupMixIn.java b/core/src/main/java/hudson/model/ViewGroupMixIn.java index c3c7fb8aa8..6f661d10c1 100644 --- a/core/src/main/java/hudson/model/ViewGroupMixIn.java +++ b/core/src/main/java/hudson/model/ViewGroupMixIn.java @@ -30,8 +30,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Implements {@link ViewGroup} to be used as a "mix-in". @@ -64,7 +64,7 @@ public abstract class ViewGroupMixIn { /** * Returns all views in the group. This list must be modifiable and concurrently iterable. */ - @Nonnull + @NonNull protected abstract List views(); /** @@ -85,17 +85,17 @@ public abstract class ViewGroupMixIn { this.owner = owner; } - public void addView(@Nonnull View v) throws IOException { + public void addView(@NonNull View v) throws IOException { v.owner = owner; views().add(v); owner.save(); } - public boolean canDelete(@Nonnull View view) { + public boolean canDelete(@NonNull View view) { return !view.isDefault(); // Cannot delete primary view } - public synchronized void deleteView(@Nonnull View view) throws IOException { + public synchronized void deleteView(@NonNull View view) throws IOException { if (views().size() <= 1) throw new IllegalStateException("Cannot delete last view"); views().remove(view); diff --git a/core/src/main/java/hudson/model/ViewJob.java b/core/src/main/java/hudson/model/ViewJob.java index 492d6eddd0..765843d581 100644 --- a/core/src/main/java/hudson/model/ViewJob.java +++ b/core/src/main/java/hudson/model/ViewJob.java @@ -60,9 +60,9 @@ public abstract class ViewJob, RunT extends Run< /** * All {@link Run}s. Copy-on-write semantics. */ - protected transient /*almost final*/ RunMap runs = new RunMap<>(); + protected transient volatile /*almost final*/ RunMap runs = new RunMap<>(null, null); - private transient boolean notLoaded = true; + private transient volatile boolean notLoaded = true; /** * If the reloading of runs are in progress (in another thread, diff --git a/core/src/main/java/hudson/model/WorkspaceBrowser.java b/core/src/main/java/hudson/model/WorkspaceBrowser.java index cc685bb061..d0c0dac088 100644 --- a/core/src/main/java/hudson/model/WorkspaceBrowser.java +++ b/core/src/main/java/hudson/model/WorkspaceBrowser.java @@ -28,7 +28,7 @@ import hudson.ExtensionPoint; import hudson.FilePath; import hudson.slaves.WorkspaceList; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Allows to access a workspace as an alternative to online build node. diff --git a/core/src/main/java/hudson/model/WorkspaceCleanupThread.java b/core/src/main/java/hudson/model/WorkspaceCleanupThread.java index 865da597c6..185be158c0 100644 --- a/core/src/main/java/hudson/model/WorkspaceCleanupThread.java +++ b/core/src/main/java/hudson/model/WorkspaceCleanupThread.java @@ -36,7 +36,7 @@ import java.util.Date; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; import jenkins.model.ModifiableTopLevelItemGroup; import org.jenkinsci.Symbol; @@ -90,7 +90,10 @@ public class WorkspaceCleanupThread extends AsyncPeriodicWork { listener.getLogger().println("Deleting " + ws + " on " + node.getDisplayName()); try { ws.deleteRecursive(); - WorkspaceList.tempDir(ws).deleteRecursive(); + FilePath tempDir = WorkspaceList.tempDir(ws); + if (tempDir != null) { + tempDir.deleteRecursive(); + } } catch (IOException | InterruptedException x) { Functions.printStackTrace(x, listener.error("Failed to delete " + ws + " on " + node.getDisplayName())); } @@ -99,7 +102,7 @@ public class WorkspaceCleanupThread extends AsyncPeriodicWork { } } - private boolean shouldBeDeleted(@Nonnull TopLevelItem item, FilePath dir, @Nonnull Node n) throws IOException, InterruptedException { + private boolean shouldBeDeleted(@NonNull TopLevelItem item, FilePath dir, @NonNull Node n) throws IOException, InterruptedException { // TODO: the use of remoting is not optimal. // One remoting can execute "exists", "lastModified", and "delete" all at once. // (Could even invert master loop so that one FileCallable takes care of all known items.) diff --git a/core/src/main/java/hudson/model/labels/LabelAssignmentAction.java b/core/src/main/java/hudson/model/labels/LabelAssignmentAction.java index e51a4028be..ef65176e23 100644 --- a/core/src/main/java/hudson/model/labels/LabelAssignmentAction.java +++ b/core/src/main/java/hudson/model/labels/LabelAssignmentAction.java @@ -7,7 +7,7 @@ import hudson.model.Queue; import hudson.model.Queue.QueueDecisionHandler; import hudson.model.Queue.Task; import hudson.model.queue.SubTask; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * {@link Action} that can be submitted to {@link Queue} that controls where @@ -36,5 +36,5 @@ public interface LabelAssignmentAction extends Action { * null to let other {@link LabelAssignmentAction}s take control, eventually to {@link SubTask#getAssignedLabel()}. * If non-null value is returned, that label will be authoritative. */ - Label getAssignedLabel(@Nonnull SubTask task); + Label getAssignedLabel(@NonNull SubTask task); } diff --git a/core/src/main/java/hudson/model/labels/LabelAtom.java b/core/src/main/java/hudson/model/labels/LabelAtom.java index c7c4d525d0..16ec68c435 100644 --- a/core/src/main/java/hudson/model/labels/LabelAtom.java +++ b/core/src/main/java/hudson/model/labels/LabelAtom.java @@ -56,8 +56,8 @@ import java.util.Set; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.Nullable; /** * Atomic single token label, like "foo" or "bar". diff --git a/core/src/main/java/hudson/model/listeners/RunListener.java b/core/src/main/java/hudson/model/listeners/RunListener.java index e0bbb2a3c7..fce83e8e84 100644 --- a/core/src/main/java/hudson/model/listeners/RunListener.java +++ b/core/src/main/java/hudson/model/listeners/RunListener.java @@ -48,7 +48,7 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Receives notifications about builds. @@ -91,7 +91,7 @@ public abstract class RunListener implements ExtensionPoint { * Any exception/error thrown from this method will be swallowed to prevent broken listeners * from breaking all the builds. */ - public void onCompleted(R r, @Nonnull TaskListener listener) {} + public void onCompleted(R r, @NonNull TaskListener listener) {} /** * Called after a build is moved to the {@code Run.State.COMPLETED} state. @@ -202,7 +202,7 @@ public abstract class RunListener implements ExtensionPoint { /** * Fires the {@link #onCompleted(Run, TaskListener)} event. */ - public static void fireCompleted(Run r, @Nonnull TaskListener listener) { + public static void fireCompleted(Run r, @NonNull TaskListener listener) { for (RunListener l : all()) { if(l.targetType.isInstance(r)) try { diff --git a/core/src/main/java/hudson/model/listeners/SCMListener.java b/core/src/main/java/hudson/model/listeners/SCMListener.java index 7bc27b6be1..c3412df090 100644 --- a/core/src/main/java/hudson/model/listeners/SCMListener.java +++ b/core/src/main/java/hudson/model/listeners/SCMListener.java @@ -41,7 +41,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import jenkins.model.Jenkins; /** diff --git a/core/src/main/java/hudson/model/queue/CauseOfBlockage.java b/core/src/main/java/hudson/model/queue/CauseOfBlockage.java index beb76b824a..c75d9ed72c 100644 --- a/core/src/main/java/hudson/model/queue/CauseOfBlockage.java +++ b/core/src/main/java/hudson/model/queue/CauseOfBlockage.java @@ -8,7 +8,7 @@ import hudson.model.Messages; import hudson.model.Label; import hudson.model.TaskListener; import hudson.slaves.Cloud; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.jvnet.localizer.Localizable; /** @@ -44,7 +44,7 @@ public abstract class CauseOfBlockage { /** * Obtains a simple implementation backed by {@link Localizable}. */ - public static CauseOfBlockage fromMessage(@Nonnull final Localizable l) { + public static CauseOfBlockage fromMessage(@NonNull final Localizable l) { l.getKey(); // null check return new CauseOfBlockage() { @Override @@ -151,6 +151,16 @@ public abstract class CauseOfBlockage { return Messages.Queue_AllNodesOffline(label.getName()); } } + + @Override + public void print(TaskListener listener) { + if (label.isEmpty()) { + listener.getLogger().println(Messages.Queue_LabelHasNoNodes(ModelHyperlinkNote.encodeTo(label))); + } else { + listener.getLogger().println(Messages.Queue_AllNodesOffline(ModelHyperlinkNote.encodeTo(label))); + } + } + } /** @@ -187,5 +197,11 @@ public abstract class CauseOfBlockage { public String getShortDescription() { return Messages.Queue_WaitingForNextAvailableExecutorOn(label.getName()); } + + @Override + public void print(TaskListener listener) { + listener.getLogger().println(Messages.Queue_WaitingForNextAvailableExecutorOn(ModelHyperlinkNote.encodeTo(label))); + } + } } diff --git a/core/src/main/java/hudson/model/queue/Executables.java b/core/src/main/java/hudson/model/queue/Executables.java index 426c12df96..43530ed3a8 100644 --- a/core/src/main/java/hudson/model/queue/Executables.java +++ b/core/src/main/java/hudson/model/queue/Executables.java @@ -27,8 +27,8 @@ import hudson.model.Queue.Executable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Convenience methods around {@link Executable}. @@ -42,7 +42,7 @@ public class Executables { * @param e Executable * @return Discovered subtask */ - public static @Nonnull SubTask getParentOf(@Nonnull Executable e) + public static @NonNull SubTask getParentOf(@NonNull Executable e) throws Error, RuntimeException { try { return e.getParent(); diff --git a/core/src/main/java/hudson/model/queue/FutureImpl.java b/core/src/main/java/hudson/model/queue/FutureImpl.java index d7151f644c..b0e56a2199 100644 --- a/core/src/main/java/hudson/model/queue/FutureImpl.java +++ b/core/src/main/java/hudson/model/queue/FutureImpl.java @@ -34,7 +34,7 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Created when {@link hudson.model.Queue.Item} is created so that the caller can track the progress of the task. @@ -92,7 +92,7 @@ public final class FutureImpl extends AsyncFutureImpl implements Que } } - synchronized void addExecutor(@Nonnull Executor executor) { + synchronized void addExecutor(@NonNull Executor executor) { this.executors.add(executor); } } diff --git a/core/src/main/java/hudson/model/queue/QueueTaskDispatcher.java b/core/src/main/java/hudson/model/queue/QueueTaskDispatcher.java index cd5c5d5b0c..dc1fd3d2ea 100644 --- a/core/src/main/java/hudson/model/queue/QueueTaskDispatcher.java +++ b/core/src/main/java/hudson/model/queue/QueueTaskDispatcher.java @@ -33,7 +33,7 @@ import hudson.model.Queue; import hudson.model.Queue.BuildableItem; import hudson.model.Queue.Task; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Vetos the execution of a task on a node diff --git a/core/src/main/java/hudson/model/queue/QueueTaskFilter.java b/core/src/main/java/hudson/model/queue/QueueTaskFilter.java index 9bf4887247..e32073f1aa 100644 --- a/core/src/main/java/hudson/model/queue/QueueTaskFilter.java +++ b/core/src/main/java/hudson/model/queue/QueueTaskFilter.java @@ -33,7 +33,7 @@ import hudson.model.ResourceList; import java.io.IOException; import java.util.Collection; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Base class for defining filter {@link hudson.model.Queue.Task}. diff --git a/core/src/main/java/hudson/model/queue/ScheduleResult.java b/core/src/main/java/hudson/model/queue/ScheduleResult.java index 95ebf0394a..82b6e6df2a 100644 --- a/core/src/main/java/hudson/model/queue/ScheduleResult.java +++ b/core/src/main/java/hudson/model/queue/ScheduleResult.java @@ -5,7 +5,7 @@ import hudson.model.Queue; import hudson.model.Queue.Item; import hudson.model.Queue.Task; import hudson.model.Queue.WaitingItem; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Result of {@link Queue#schedule2} diff --git a/core/src/main/java/hudson/model/queue/SubTask.java b/core/src/main/java/hudson/model/queue/SubTask.java index 485bebc109..f16931759f 100644 --- a/core/src/main/java/hudson/model/queue/SubTask.java +++ b/core/src/main/java/hudson/model/queue/SubTask.java @@ -31,9 +31,9 @@ import hudson.model.Queue.Executable; import hudson.model.Queue.Task; import hudson.model.ResourceActivity; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * A component of {@link Task} that represents a computation carried out by a single {@link Executor}. @@ -90,7 +90,7 @@ public interface SubTask extends ResourceActivity { * Gets the {@link Task} that this subtask belongs to. * @return by default, {@code this} */ - default @Nonnull Task getOwnerTask() { + default @NonNull Task getOwnerTask() { return (Task) this; } diff --git a/core/src/main/java/hudson/model/queue/Tasks.java b/core/src/main/java/hudson/model/queue/Tasks.java index baf2f9714f..c591fe4940 100644 --- a/core/src/main/java/hudson/model/queue/Tasks.java +++ b/core/src/main/java/hudson/model/queue/Tasks.java @@ -26,11 +26,11 @@ package hudson.model.queue; import hudson.model.Queue; import hudson.model.Queue.Item; import hudson.model.Queue.Task; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import org.acegisecurity.Authentication; import java.util.Collection; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.security.QueueItemAuthenticator; import jenkins.security.QueueItemAuthenticatorProvider; @@ -56,7 +56,7 @@ public class Tasks { /** deprecated call {@link SubTask#getOwnerTask} directly */ @Deprecated - public static @Nonnull Task getOwnerTaskOf(@Nonnull SubTask t) { + public static @NonNull Task getOwnerTaskOf(@NonNull SubTask t) { return t.getOwnerTask(); } @@ -68,7 +68,7 @@ public class Tasks { * @since 2.55 */ @CheckForNull - public static hudson.model.Item getItemOf(@Nonnull SubTask t) { + public static hudson.model.Item getItemOf(@NonNull SubTask t) { Queue.Task p = t.getOwnerTask(); while (!(p instanceof hudson.model.Item)) { Queue.Task o = p.getOwnerTask(); @@ -82,14 +82,14 @@ public class Tasks { /** @deprecated call {@link Task#getDefaultAuthentication()} directly */ @Deprecated - @Nonnull + @NonNull public static Authentication getDefaultAuthenticationOf(Task t) { return t.getDefaultAuthentication(); } /** @deprecated call {@link Task#getDefaultAuthentication(Item)} directly */ @Deprecated - @Nonnull + @NonNull public static Authentication getDefaultAuthenticationOf(Task t, Item item) { return t.getDefaultAuthentication(item); } @@ -102,7 +102,7 @@ public class Tasks { * @return an authentication as specified by some {@link QueueItemAuthenticator#authenticate(hudson.model.Queue.Task)}; else {@link Task#getDefaultAuthentication()} * @since 1.560 */ - public static @Nonnull Authentication getAuthenticationOf(@Nonnull Task t) { + public static @NonNull Authentication getAuthenticationOf(@NonNull Task t) { for (QueueItemAuthenticator qia : QueueItemAuthenticatorProvider.authenticators()) { Authentication a = qia.authenticate(t); if (a != null) { diff --git a/core/src/main/java/hudson/model/queue/WorkUnit.java b/core/src/main/java/hudson/model/queue/WorkUnit.java index 7665c42a0e..541485b8ad 100644 --- a/core/src/main/java/hudson/model/queue/WorkUnit.java +++ b/core/src/main/java/hudson/model/queue/WorkUnit.java @@ -27,7 +27,7 @@ import hudson.model.Executor; import hudson.model.Queue; import hudson.model.Queue.Executable; import hudson.model.Queue.Task; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import hudson.model.Run; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; diff --git a/core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java b/core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java index 5b567ab8ad..b7f2ab15ff 100644 --- a/core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java +++ b/core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java @@ -7,8 +7,8 @@ import hudson.remoting.VirtualChannel; import hudson.slaves.SlaveComputer; import jenkins.model.Jenkins; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -81,7 +81,7 @@ public abstract class AbstractAsyncNodeMonitorDescriptor extends AbstractNode /** * Perform monitoring with detailed reporting. */ - protected final @Nonnull Result monitorDetailed() throws InterruptedException { + protected final @NonNull Result monitorDetailed() throws InterruptedException { Map> futures = new HashMap<>(); Set skipped = new HashSet<>(); @@ -157,15 +157,15 @@ public abstract class AbstractAsyncNodeMonitorDescriptor extends AbstractNode protected static final class Result { private static final long serialVersionUID = -7671448355804481216L; - private final @Nonnull Map data; - private final @Nonnull ArrayList skipped; + private final @NonNull Map data; + private final @NonNull ArrayList skipped; - private Result(@Nonnull Map data, @Nonnull Collection skipped) { + private Result(@NonNull Map data, @NonNull Collection skipped) { this.data = new HashMap<>(data); this.skipped = new ArrayList<>(skipped); } - protected @Nonnull Map getMonitoringData() { + protected @NonNull Map getMonitoringData() { return data; } @@ -173,7 +173,7 @@ public abstract class AbstractAsyncNodeMonitorDescriptor extends AbstractNode * Computers that ware skipped during monitoring as they either do not have a a channel (offline) or the monitor * have not produced the Callable. Computers that caused monitor to throw exception are not returned here. */ - protected @Nonnull List getSkipped() { + protected @NonNull List getSkipped() { return skipped; } } diff --git a/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java b/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java index 267a24390a..bba32306f0 100644 --- a/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java +++ b/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java @@ -34,7 +34,7 @@ import hudson.slaves.OfflineCause; import jenkins.util.SystemProperties; import jenkins.util.Timer; -import javax.annotation.concurrent.GuardedBy; +import net.jcip.annotations.GuardedBy; import java.io.IOException; import java.util.Collections; import java.util.Date; diff --git a/core/src/main/java/hudson/node_monitors/MonitorOfflineCause.java b/core/src/main/java/hudson/node_monitors/MonitorOfflineCause.java index 4b7c5b6008..77c3285cd7 100644 --- a/core/src/main/java/hudson/node_monitors/MonitorOfflineCause.java +++ b/core/src/main/java/hudson/node_monitors/MonitorOfflineCause.java @@ -23,7 +23,7 @@ */ package hudson.node_monitors; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.slaves.OfflineCause; @@ -38,5 +38,5 @@ public abstract class MonitorOfflineCause extends OfflineCause { /** * Identify monitor implementation that put computer offline. */ - public abstract @Nonnull Class getTrigger(); + public abstract @NonNull Class getTrigger(); } diff --git a/core/src/main/java/hudson/node_monitors/NodeMonitor.java b/core/src/main/java/hudson/node_monitors/NodeMonitor.java index b0e5f2fe40..bc04c75569 100644 --- a/core/src/main/java/hudson/node_monitors/NodeMonitor.java +++ b/core/src/main/java/hudson/node_monitors/NodeMonitor.java @@ -36,7 +36,7 @@ import hudson.model.Descriptor; import hudson.util.DescriptorList; import java.util.List; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; diff --git a/core/src/main/java/hudson/os/WindowsUtil.java b/core/src/main/java/hudson/os/WindowsUtil.java index 77f72b6c4a..116820fac2 100644 --- a/core/src/main/java/hudson/os/WindowsUtil.java +++ b/core/src/main/java/hudson/os/WindowsUtil.java @@ -27,7 +27,7 @@ package hudson.os; import hudson.Functions; import org.apache.commons.io.IOUtils; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.File; import java.io.IOException; import java.util.Arrays; @@ -50,7 +50,7 @@ public class WindowsUtil { * @param argument argument to be quoted or escaped for windows shells. * @return properly quoted and escaped windows arguments. */ - public static @Nonnull String quoteArgument(@Nonnull String argument) { + public static @NonNull String quoteArgument(@NonNull String argument) { if (!NEEDS_QUOTING.matcher(argument).find()) return argument; StringBuilder sb = new StringBuilder(); sb.append('"'); @@ -89,7 +89,7 @@ public class WindowsUtil { * @param argument argument to be quoted or escaped for {@code cmd.exe}. * @return properly quoted and escaped arguments to {@code cmd.exe}. */ - public static @Nonnull String quoteArgumentForCmd(@Nonnull String argument) { + public static @NonNull String quoteArgumentForCmd(@NonNull String argument) { return CMD_METACHARS.matcher(quoteArgument(argument)).replaceAll("^$0"); } @@ -98,7 +98,7 @@ public class WindowsUtil { * @param argv arguments to be quoted or escaped for {@code cmd.exe /C ...}. * @return properly quoted and escaped arguments to {@code cmd.exe /C ...}. */ - public static @Nonnull Process execCmd(String... argv) throws IOException { + public static @NonNull Process execCmd(String... argv) throws IOException { String command = Arrays.stream(argv).map(WindowsUtil::quoteArgumentForCmd).collect(Collectors.joining(" ")); return Runtime.getRuntime().exec(new String[]{"cmd.exe", "/C", command}); } @@ -113,7 +113,7 @@ public class WindowsUtil { * @throws InterruptedException if the call to mklink is interrupted before completing * @throws UnsupportedOperationException if this method is called on a non-Windows platform */ - public static @Nonnull File createJunction(@Nonnull File junction, @Nonnull File target) throws IOException, InterruptedException { + public static @NonNull File createJunction(@NonNull File junction, @NonNull File target) throws IOException, InterruptedException { if (!Functions.isWindows()) { throw new UnsupportedOperationException("Can only be called on windows platform"); } diff --git a/core/src/main/java/hudson/scheduler/CronTab.java b/core/src/main/java/hudson/scheduler/CronTab.java index 77118499ad..8b2d507b9f 100644 --- a/core/src/main/java/hudson/scheduler/CronTab.java +++ b/core/src/main/java/hudson/scheduler/CronTab.java @@ -34,7 +34,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.util.Calendar.*; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Table for driving scheduled tasks. diff --git a/core/src/main/java/hudson/scheduler/CronTabList.java b/core/src/main/java/hudson/scheduler/CronTabList.java index edeb6e0a37..97dd53dd76 100644 --- a/core/src/main/java/hudson/scheduler/CronTabList.java +++ b/core/src/main/java/hudson/scheduler/CronTabList.java @@ -28,8 +28,8 @@ import java.util.Calendar; import java.util.TimeZone; import java.util.Collection; import java.util.Vector; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -92,11 +92,11 @@ public final class CronTabList { return null; } - public static CronTabList create(@Nonnull String format) throws ANTLRException { + public static CronTabList create(@NonNull String format) throws ANTLRException { return create(format,null); } - public static CronTabList create(@Nonnull String format, Hash hash) throws ANTLRException { + public static CronTabList create(@NonNull String format, Hash hash) throws ANTLRException { Vector r = new Vector<>(); int lineNumber = 0; String timezone = null; diff --git a/core/src/main/java/hudson/scm/PollingResult.java b/core/src/main/java/hudson/scm/PollingResult.java index 39b3a0caba..718b98cfc2 100644 --- a/core/src/main/java/hudson/scm/PollingResult.java +++ b/core/src/main/java/hudson/scm/PollingResult.java @@ -5,8 +5,8 @@ import hudson.model.TaskListener; import hudson.Launcher; import hudson.FilePath; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.Serializable; /** @@ -41,7 +41,7 @@ public final class PollingResult implements Serializable { * the immediate rebuild, and (2) allow SCM to ignore some changes in the repository to implement * exclusion feature. */ - public final @Nonnull Change change; + public final @NonNull Change change; /** * Degree of changes between the previous state and this state. @@ -79,14 +79,14 @@ public final class PollingResult implements Serializable { INCOMPARABLE } - public PollingResult(@CheckForNull SCMRevisionState baseline, @CheckForNull SCMRevisionState remote, @Nonnull Change change) { + public PollingResult(@CheckForNull SCMRevisionState baseline, @CheckForNull SCMRevisionState remote, @NonNull Change change) { if (change==null) throw new IllegalArgumentException(); this.baseline = baseline; this.remote = remote; this.change = change; } - public PollingResult(@Nonnull Change change) { + public PollingResult(@NonNull Change change) { this(null,null,change); } diff --git a/core/src/main/java/hudson/scm/SCM.java b/core/src/main/java/hudson/scm/SCM.java index 13e66c3873..63180e9691 100644 --- a/core/src/main/java/hudson/scm/SCM.java +++ b/core/src/main/java/hudson/scm/SCM.java @@ -56,9 +56,9 @@ import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import jenkins.model.Jenkins; import jenkins.util.SystemProperties; import org.kohsuke.stapler.export.Exported; @@ -232,7 +232,7 @@ public abstract class SCM implements Describable, ExtensionPoint { * * @since 1.568 */ - public boolean processWorkspaceBeforeDeletion(@Nonnull Job project, @Nonnull FilePath workspace, @Nonnull Node node) throws IOException, InterruptedException { + public boolean processWorkspaceBeforeDeletion(@NonNull Job project, @NonNull FilePath workspace, @NonNull Node node) throws IOException, InterruptedException { if (project instanceof AbstractProject) { return processWorkspaceBeforeDeletion((AbstractProject) project, workspace, node); } else { @@ -329,7 +329,7 @@ public abstract class SCM implements Describable, ExtensionPoint { * this exception should be simply propagated all the way up. * @since 1.568 */ - public @CheckForNull SCMRevisionState calcRevisionsFromBuild(@Nonnull Run build, @Nullable FilePath workspace, @Nullable Launcher launcher, @Nonnull TaskListener listener) throws IOException, InterruptedException { + public @CheckForNull SCMRevisionState calcRevisionsFromBuild(@NonNull Run build, @Nullable FilePath workspace, @Nullable Launcher launcher, @NonNull TaskListener listener) throws IOException, InterruptedException { if (build instanceof AbstractBuild && Util.isOverridden(SCM.class, getClass(), "calcRevisionsFromBuild", AbstractBuild.class, Launcher.class, TaskListener.class)) { return calcRevisionsFromBuild((AbstractBuild) build, launcher, listener); } else { @@ -388,7 +388,7 @@ public abstract class SCM implements Describable, ExtensionPoint { * this exception should be simply propagated all the way up. * @since 1.568 */ - public PollingResult compareRemoteRevisionWith(@Nonnull Job project, @Nullable Launcher launcher, @Nullable FilePath workspace, @Nonnull TaskListener listener, @Nonnull SCMRevisionState baseline) throws IOException, InterruptedException { + public PollingResult compareRemoteRevisionWith(@NonNull Job project, @Nullable Launcher launcher, @Nullable FilePath workspace, @NonNull TaskListener listener, @NonNull SCMRevisionState baseline) throws IOException, InterruptedException { if (project instanceof AbstractProject && Util.isOverridden(SCM.class, getClass(), "compareRemoteRevisionWith", AbstractProject.class, Launcher.class, FilePath.class, TaskListener.class, SCMRevisionState.class)) { return compareRemoteRevisionWith((AbstractProject) project, launcher, workspace, listener, baseline); } else { @@ -446,7 +446,7 @@ public abstract class SCM implements Describable, ExtensionPoint { * @return by default, just {@link #getType} * @since 1.568 */ - public @Nonnull String getKey() { + public @NonNull String getKey() { return getType(); } @@ -478,7 +478,7 @@ public abstract class SCM implements Describable, ExtensionPoint { * @throws AbortException in case of a routine failure * @since 1.568 */ - public void checkout(@Nonnull Run build, @Nonnull Launcher launcher, @Nonnull FilePath workspace, @Nonnull TaskListener listener, @CheckForNull File changelogFile, @CheckForNull SCMRevisionState baseline) throws IOException, InterruptedException { + public void checkout(@NonNull Run build, @NonNull Launcher launcher, @NonNull FilePath workspace, @NonNull TaskListener listener, @CheckForNull File changelogFile, @CheckForNull SCMRevisionState baseline) throws IOException, InterruptedException { if (build instanceof AbstractBuild && listener instanceof BuildListener && Util.isOverridden(SCM.class, getClass(), "checkout", AbstractBuild.class, Launcher.class, FilePath.class, BuildListener.class, File.class)) { if (changelogFile == null) { changelogFile = File.createTempFile("changelog", ".xml"); @@ -500,7 +500,7 @@ public abstract class SCM implements Describable, ExtensionPoint { } @Deprecated - public boolean checkout(AbstractBuild build, Launcher launcher, FilePath workspace, BuildListener listener, @Nonnull File changelogFile) throws IOException, InterruptedException { + public boolean checkout(AbstractBuild build, Launcher launcher, FilePath workspace, BuildListener listener, @NonNull File changelogFile) throws IOException, InterruptedException { AbstractBuild prev = build.getPreviousBuild(); checkout((Run) build, launcher, workspace, listener, changelogFile, prev != null ? prev.getAction(SCMRevisionState.class) : null); return true; @@ -510,7 +510,7 @@ public abstract class SCM implements Describable, ExtensionPoint { * Get a chance to do operations after the workspace i checked out and the changelog is written. * @since 1.568 */ - public void postCheckout(@Nonnull Run build, @Nonnull Launcher launcher, @Nonnull FilePath workspace, @Nonnull TaskListener listener) throws IOException, InterruptedException { + public void postCheckout(@NonNull Run build, @NonNull Launcher launcher, @NonNull FilePath workspace, @NonNull TaskListener listener) throws IOException, InterruptedException { if (build instanceof AbstractBuild && listener instanceof BuildListener) { postCheckout((AbstractBuild) build, launcher, workspace, (BuildListener) listener); } @@ -539,7 +539,7 @@ public abstract class SCM implements Describable, ExtensionPoint { * * @since 2.60 */ - public void buildEnvironment(@Nonnull Run build, @Nonnull Map env) { + public void buildEnvironment(@NonNull Run build, @NonNull Map env) { if (build instanceof AbstractBuild) { buildEnvVars((AbstractBuild)build, env); } @@ -705,7 +705,7 @@ public abstract class SCM implements Describable, ExtensionPoint { /** * @since 1.568 */ - protected final void createEmptyChangeLog(@Nonnull File changelogFile, @Nonnull TaskListener listener, @Nonnull String rootTag) throws IOException { + protected final void createEmptyChangeLog(@NonNull File changelogFile, @NonNull TaskListener listener, @NonNull String rootTag) throws IOException { try (FileWriter w = new FileWriter(changelogFile)) { w.write("<"+rootTag +"/>"); } diff --git a/core/src/main/java/hudson/search/CollectionSearchIndex.java b/core/src/main/java/hudson/search/CollectionSearchIndex.java index 7bbbc609ee..3f60912381 100644 --- a/core/src/main/java/hudson/search/CollectionSearchIndex.java +++ b/core/src/main/java/hudson/search/CollectionSearchIndex.java @@ -27,7 +27,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * {@link SearchIndex} built on a {@link Map}. @@ -46,7 +46,7 @@ public abstract class CollectionSearchIndex i */ protected abstract Collection all(); - @Nonnull + @NonNull protected Iterable allAsIterable() { Collection all = all(); return all == null ? Collections.emptySet() : all; diff --git a/core/src/main/java/hudson/search/Search.java b/core/src/main/java/hudson/search/Search.java index e21014a8d4..8c2ac966ed 100644 --- a/core/src/main/java/hudson/search/Search.java +++ b/core/src/main/java/hudson/search/Search.java @@ -38,7 +38,7 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import javax.servlet.ServletException; import jenkins.util.MemoryReductionUtil; diff --git a/core/src/main/java/hudson/security/ACL.java b/core/src/main/java/hudson/security/ACL.java index e02083e6aa..963fd398b7 100644 --- a/core/src/main/java/hudson/security/ACL.java +++ b/core/src/main/java/hudson/security/ACL.java @@ -27,8 +27,11 @@ import hudson.model.User; import hudson.model.View; import hudson.model.ViewDescriptor; import hudson.model.ViewGroup; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.stream.Collectors; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.model.Item; import hudson.remoting.Callable; @@ -64,7 +67,7 @@ public abstract class ACL { * @throws AccessDeniedException * if the user doesn't have the permission. */ - public final void checkPermission(@Nonnull Permission p) { + public final void checkPermission(@NonNull Permission p) { Authentication a = Jenkins.getAuthentication(); if (a == SYSTEM) { return; @@ -77,13 +80,56 @@ public abstract class ACL { } } + /** + * Checks if the current security principal has one of the supplied permissions. + * + * This is just a convenience function. + * + * @throws AccessDeniedException + * if the user doesn't have the permission. + * @throws IllegalArgumentException + * if no permissions are provided + * + * @since 2.222 + */ + public final void checkAnyPermission(@NonNull Permission... permissions) { + if (permissions.length == 0) { + throw new IllegalArgumentException("At least one permission must be provided"); + } + + boolean failed = !hasAnyPermission(permissions); + + Authentication authentication = Jenkins.getAuthentication(); + if (failed) { // we know that none of the permissions are granted + Set enabledPermissions = new LinkedHashSet<>(); + for (Permission p : permissions) { + while (!p.enabled && p.impliedBy != null) { + p = p.impliedBy; + } + enabledPermissions.add(p); + } + String permissionsDisplayName = enabledPermissions.stream() + .map(p -> p.group.title + "/" + p.name) + .collect(Collectors.joining(", ")); + + String errorMessage; + if (enabledPermissions.size() == 1) { + errorMessage = Messages.AccessDeniedException2_MissingPermission(authentication.getName(), permissionsDisplayName); + } else { + errorMessage = Messages.AccessDeniedException_MissingPermissions(authentication.getName(), permissionsDisplayName); + } + + throw new AccessDeniedException(errorMessage); + } + } + /** * Checks if the current security principal has this permission. * * @return false * if the user doesn't have the permission. */ - public final boolean hasPermission(@Nonnull Permission p) { + public final boolean hasPermission(@NonNull Permission p) { Authentication a = Jenkins.getAuthentication(); if (a == SYSTEM) { return true; @@ -91,6 +137,33 @@ public abstract class ACL { return hasPermission(a, p); } + /** + * Checks if the current security principal has any of the permissions. + * + * @return {@code false} + * if the user doesn't have one of the required permissions. + * + * @throws IllegalArgumentException + * if no permissions are provided + */ + public final boolean hasAnyPermission(@NonNull Permission... permissions) { + if (permissions.length == 0) { + throw new IllegalArgumentException("At least one permission must be provided"); + } + + Authentication a = Jenkins.getAuthentication(); + if (a == SYSTEM) { + return true; + } + + for (Permission permission : permissions) { + if (hasPermission(permission)) { + return true; + } + } + return false; + } + /** * Checks if the given principle has the given permission. * @@ -98,7 +171,7 @@ public abstract class ACL { * Note that {@link #SYSTEM} can be passed in as the authentication parameter, * in which case you should probably just assume it has every permission. */ - public abstract boolean hasPermission(@Nonnull Authentication a, @Nonnull Permission permission); + public abstract boolean hasPermission(@NonNull Authentication a, @NonNull Permission permission); /** * Creates a simple {@link ACL} implementation based on a “single-abstract-method” easily implemented via lambda syntax. @@ -126,8 +199,8 @@ public abstract class ACL { * if the user doesn't have the permission. * @since 1.607 */ - public final void checkCreatePermission(@Nonnull ItemGroup c, - @Nonnull TopLevelItemDescriptor d) { + public final void checkCreatePermission(@NonNull ItemGroup c, + @NonNull TopLevelItemDescriptor d) { Authentication a = Jenkins.getAuthentication(); if (a == SYSTEM) { return; @@ -149,8 +222,8 @@ public abstract class ACL { * if the user doesn't have the permission. * @since 1.607 */ - public boolean hasCreatePermission(@Nonnull Authentication a, @Nonnull ItemGroup c, - @Nonnull TopLevelItemDescriptor d) { + public boolean hasCreatePermission(@NonNull Authentication a, @NonNull ItemGroup c, + @NonNull TopLevelItemDescriptor d) { return true; } @@ -164,8 +237,8 @@ public abstract class ACL { * @throws AccessDeniedException if the user doesn't have the permission. * @since 1.607 */ - public final void checkCreatePermission(@Nonnull ViewGroup c, - @Nonnull ViewDescriptor d) { + public final void checkCreatePermission(@NonNull ViewGroup c, + @NonNull ViewDescriptor d) { Authentication a = Jenkins.getAuthentication(); if (a == SYSTEM) { return; @@ -188,8 +261,8 @@ public abstract class ACL { * if the user doesn't have the permission. * @since 2.37 */ - public boolean hasCreatePermission(@Nonnull Authentication a, @Nonnull ViewGroup c, - @Nonnull ViewDescriptor d) { + public boolean hasCreatePermission(@NonNull Authentication a, @NonNull ViewGroup c, + @NonNull ViewDescriptor d) { return true; } @@ -257,7 +330,7 @@ public abstract class ACL { * @deprecated use try with resources and {@link #as(Authentication)} */ @Deprecated - public static @Nonnull SecurityContext impersonate(@Nonnull Authentication auth) { + public static @NonNull SecurityContext impersonate(@NonNull Authentication auth) { SecurityContext old = SecurityContextHolder.getContext(); SecurityContextHolder.setContext(new NonSerializableSecurityContext(auth)); return old; @@ -271,7 +344,7 @@ public abstract class ACL { * @deprecated use try with resources and {@link #as(Authentication)} */ @Deprecated - public static void impersonate(@Nonnull Authentication auth, @Nonnull Runnable body) { + public static void impersonate(@NonNull Authentication auth, @NonNull Runnable body) { SecurityContext old = impersonate(auth); try { body.run(); @@ -312,8 +385,8 @@ public abstract class ACL { * @return the previous authentication context * @since 2.14 */ - @Nonnull - public static ACLContext as(@Nonnull Authentication auth) { + @NonNull + public static ACLContext as(@NonNull Authentication auth) { final ACLContext context = new ACLContext(SecurityContextHolder.getContext()); SecurityContextHolder.setContext(new NonSerializableSecurityContext(auth)); return context; @@ -335,7 +408,7 @@ public abstract class ACL { * @return the previous authentication context * @since 2.14 */ - @Nonnull + @NonNull public static ACLContext as(@CheckForNull User user) { return as(user == null ? Jenkins.ANONYMOUS : user.impersonate()); } @@ -345,7 +418,7 @@ public abstract class ACL { * @see Jenkins#ANONYMOUS * @see AnonymousAuthenticationToken */ - public static boolean isAnonymous(@Nonnull Authentication authentication) { + public static boolean isAnonymous(@NonNull Authentication authentication) { //TODO use AuthenticationTrustResolver instead to be consistent through the application return authentication instanceof AnonymousAuthenticationToken; } diff --git a/core/src/main/java/hudson/security/ACLContext.java b/core/src/main/java/hudson/security/ACLContext.java index ffcdf4b951..2f1b4e207d 100644 --- a/core/src/main/java/hudson/security/ACLContext.java +++ b/core/src/main/java/hudson/security/ACLContext.java @@ -23,7 +23,7 @@ */ package hudson.security; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.acegisecurity.Authentication; import org.acegisecurity.context.SecurityContext; import org.acegisecurity.context.SecurityContextHolder; @@ -38,14 +38,14 @@ public class ACLContext implements AutoCloseable { /** * The previous context. */ - @Nonnull + @NonNull private final SecurityContext previousContext; /** * Private constructor to ensure only instance creation is from {@link ACL#as(Authentication)}. * @param previousContext the previous context */ - ACLContext(@Nonnull SecurityContext previousContext) { + ACLContext(@NonNull SecurityContext previousContext) { this.previousContext = previousContext; } @@ -53,7 +53,7 @@ public class ACLContext implements AutoCloseable { * Accessor for the previous context. * @return the previous context. */ - @Nonnull + @NonNull public SecurityContext getPreviousContext() { return previousContext; } diff --git a/core/src/main/java/hudson/security/AccessControlled.java b/core/src/main/java/hudson/security/AccessControlled.java index 9aa084df2b..5e12debb3c 100644 --- a/core/src/main/java/hudson/security/AccessControlled.java +++ b/core/src/main/java/hudson/security/AccessControlled.java @@ -23,7 +23,7 @@ */ package hudson.security; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.acegisecurity.AccessDeniedException; import org.acegisecurity.Authentication; @@ -38,27 +38,47 @@ public interface AccessControlled { * * @return never null. */ - @Nonnull ACL getACL(); + @NonNull ACL getACL(); /** * Convenient short-cut for {@code getACL().checkPermission(permission)} */ - default void checkPermission(@Nonnull Permission permission) throws AccessDeniedException { + default void checkPermission(@NonNull Permission permission) throws AccessDeniedException { getACL().checkPermission(permission); } + /** + * Convenient short-cut for {@code getACL().checkAnyPermission(permission)} + * @see ACL#checkAnyPermission(Permission...) + * + * @since 2.222 + */ + default void checkAnyPermission(@NonNull Permission... permission) throws AccessDeniedException { + getACL().checkAnyPermission(permission); + } + /** * Convenient short-cut for {@code getACL().hasPermission(permission)} */ - default boolean hasPermission(@Nonnull Permission permission) { + default boolean hasPermission(@NonNull Permission permission) { return getACL().hasPermission(permission); } + /** + * Convenient short-cut for {@code getACL().hasAnyPermission(permission)} + * @see ACL#hasAnyPermission(Permission...) + * + * @since 2.222 + */ + default boolean hasAnyPermission(@NonNull Permission... permission) { + return getACL().hasAnyPermission(permission); + } + /** * Convenient short-cut for {@code getACL().hasPermission(a, permission)} * @since 2.92 */ - default boolean hasPermission(@Nonnull Authentication a, @Nonnull Permission permission) { + default boolean hasPermission(@NonNull Authentication a, @NonNull Permission permission) { if (a == ACL.SYSTEM) { return true; } diff --git a/core/src/main/java/hudson/security/AuthorizationStrategy.java b/core/src/main/java/hudson/security/AuthorizationStrategy.java index e95b0c426f..3d63272d0f 100644 --- a/core/src/main/java/hudson/security/AuthorizationStrategy.java +++ b/core/src/main/java/hudson/security/AuthorizationStrategy.java @@ -35,7 +35,7 @@ import net.sf.json.JSONObject; import org.jenkinsci.Symbol; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.Serializable; import java.util.Collection; import java.util.Collections; @@ -68,18 +68,18 @@ public abstract class AuthorizationStrategy extends AbstractDescribableImpl * IOW, this ACL will have the ultimate say on the access control. */ - public abstract @Nonnull ACL getRootACL(); + public abstract @NonNull ACL getRootACL(); /** * @deprecated since 1.277 * Override {@link #getACL(Job)} instead. */ @Deprecated - public @Nonnull ACL getACL(@Nonnull AbstractProject project) { + public @NonNull ACL getACL(@NonNull AbstractProject project) { return getACL((Job)project); } - public @Nonnull ACL getACL(@Nonnull Job project) { + public @NonNull ACL getACL(@NonNull Job project) { return getRootACL(); } @@ -93,7 +93,7 @@ public abstract class AuthorizationStrategy extends AbstractDescribableImpl { ACL base = item.getOwner().getACL(); @@ -115,7 +115,7 @@ public abstract class AuthorizationStrategy extends AbstractDescribableImpl getGroups(); + public abstract @NonNull Collection getGroups(); /** * Returns all the registered {@link AuthorizationStrategy} descriptors. */ - public static @Nonnull DescriptorExtensionList> all() { + public static @NonNull DescriptorExtensionList> all() { return Jenkins.get().getDescriptorList(AuthorizationStrategy.class); } @@ -212,12 +212,12 @@ public abstract class AuthorizationStrategy extends AbstractDescribableImpl getGroups() { + public @NonNull Collection getGroups() { return Collections.emptySet(); } @@ -231,7 +231,7 @@ public abstract class AuthorizationStrategy extends AbstractDescribableImpl getUserPropertyClass(); /** @@ -121,7 +121,7 @@ public abstract class FederatedLoginService implements ExtensionPoint { * * @return must not be null. */ - @Nonnull + @NonNull public abstract String getIdentifier(); /** @@ -184,7 +184,7 @@ public abstract class FederatedLoginService implements ExtensionPoint { * a user registration session (provided that {@link SecurityRealm} supports that.) */ @SuppressWarnings("ACL.impersonate") - @Nonnull + @NonNull public User signin() throws UnclaimedIdentityException { User u = locateUser(); if (u!=null) { diff --git a/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java b/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java index f5ba2c38b5..8befb0a77f 100644 --- a/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java +++ b/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java @@ -23,10 +23,10 @@ */ package hudson.security; -import com.google.common.base.Predicate; import hudson.BulkChange; import hudson.Extension; import hudson.Functions; +import hudson.RestrictedSince; import hudson.markup.MarkupFormatter; import hudson.model.Descriptor; import hudson.model.Descriptor.FormException; @@ -37,9 +37,11 @@ import hudson.util.FormApply; import java.io.IOException; import java.util.Set; import java.util.TreeSet; +import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import jenkins.model.GlobalConfigurationCategory; @@ -92,6 +94,12 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr return Jenkins.get().isDisableRememberMe(); } + @NonNull + @Override + public Category getCategory() { + return Category.SECURITY; + } + @POST public synchronized void doConfigure(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException { // for compatibility reasons, the actual value is stored in Jenkins @@ -106,7 +114,7 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr } } - public boolean configure(StaplerRequest req, JSONObject json) throws hudson.model.Descriptor.FormException { + public boolean configure(StaplerRequest req, JSONObject json) throws FormException { // for compatibility reasons, the actual value is stored in Jenkins Jenkins j = Jenkins.get(); j.checkPermission(Jenkins.ADMINISTER); @@ -126,7 +134,7 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr try { j.setSlaveAgentPort(new ServerTcpPort(json.getJSONObject("slaveAgentPort")).getPort()); } catch (IOException e) { - throw new hudson.model.Descriptor.FormException(e, "slaveAgentPortType"); + throw new FormException(e, "slaveAgentPortType"); } } Set agentProtocols = new TreeSet<>(); @@ -144,7 +152,7 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr // persist all the additional security configs boolean result = true; - for(Descriptor d : Functions.getSortedDescriptorsForGlobalConfig(FILTER)){ + for(Descriptor d : Functions.getSortedDescriptorsForGlobalConfigByDescriptor(FILTER)){ result &= configureDescriptor(req,json,d); } @@ -181,18 +189,16 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr @Override public Permission getRequiredPermission() { - return Jenkins.ADMINISTER; + return Jenkins.SYSTEM_READ; } - public static Predicate FILTER = new Predicate() { - public boolean apply(GlobalConfigurationCategory input) { - return input instanceof GlobalConfigurationCategory.Security; - } - }; + @Restricted(NoExternalUse.class) + @RestrictedSince("TODO") + public static Predicate FILTER = input -> input.getCategory() instanceof GlobalConfigurationCategory.Security; /** * @return - * @see hudson.model.Describable#getDescriptor() + * @see Describable#getDescriptor() */ @SuppressWarnings("unchecked") @Override diff --git a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java index bbfd617ae6..7bf261b201 100644 --- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java +++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java @@ -30,6 +30,7 @@ import hudson.ExtensionList; import hudson.Util; import hudson.diagnosis.OldDataMonitor; import hudson.model.Descriptor; +import hudson.util.FormValidation; import jenkins.model.Jenkins; import hudson.model.ManagementLink; import hudson.model.ModelObject; @@ -61,6 +62,7 @@ import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.ForwardToView; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.HttpResponses; +import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -68,7 +70,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST; import org.mindrot.jbcrypt.BCrypt; import org.springframework.dao.DataAccessException; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -251,7 +253,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea private User _doCreateAccount(StaplerRequest req, StaplerResponse rsp, String formView) throws ServletException, IOException { if(!allowsSignup()) - throw HttpResponses.error(SC_UNAUTHORIZED,new Exception("User sign up is prohibited")); + throw HttpResponses.errorWithoutStack(SC_UNAUTHORIZED, "User sign up is prohibited"); boolean firstUser = !hasSomeUser(); User u = createAccount(req, rsp, enableCaptcha, formView); @@ -482,7 +484,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea return user; } - private boolean containsOnlyAcceptableCharacters(@Nonnull String value){ + private boolean containsOnlyAcceptableCharacters(@NonNull String value){ if(ID_REGEX == null){ return value.matches(DEFAULT_ID_REGEX); }else{ @@ -771,6 +773,12 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea public String getDescription() { return Messages.HudsonPrivateSecurityRealm_ManageUserLinks_Description(); } + + @NonNull + @Override + public Category getCategory() { + return Category.SECURITY; + } } /** @@ -911,6 +919,13 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea public String getDisplayName() { return Messages.HudsonPrivateSecurityRealm_DisplayName(); } + + public FormValidation doCheckAllowsSignup(@QueryParameter boolean value) { + if (value) { + return FormValidation.warning(Messages.HudsonPrivateSecurityRealm_SignupWarning()); + } + return FormValidation.ok(); + } } private static final Filter CREATE_FIRST_USER_FILTER = new Filter() { diff --git a/core/src/main/java/hudson/security/Permission.java b/core/src/main/java/hudson/security/Permission.java index 703c3016c7..c0de58cd1a 100644 --- a/core/src/main/java/hudson/security/Permission.java +++ b/core/src/main/java/hudson/security/Permission.java @@ -33,8 +33,8 @@ import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.jvnet.localizer.Localizable; @@ -56,14 +56,14 @@ public final class Permission { // break eclipse compilation //Override - public int compare(@Nonnull Permission one, @Nonnull Permission two) { + public int compare(@NonNull Permission one, @NonNull Permission two) { return one.getId().compareTo(two.getId()); } }; - public final @Nonnull Class owner; + public final @NonNull Class owner; - public final @Nonnull PermissionGroup group; + public final @NonNull PermissionGroup group; // if some plugin serialized old version of this class using XStream, `id` can be null private final @CheckForNull String id; @@ -77,7 +77,7 @@ public final class Permission { *

* The expected naming convention is something like "BrowseWorkspace". */ - public final @Nonnull String name; + public final @NonNull String name; /** * Human-readable description of this permission. @@ -119,7 +119,7 @@ public final class Permission { /** * Scopes that this permission is directly contained by. */ - private final @Nonnull Set scopes; + private final @NonNull Set scopes; /** * Defines a new permission. @@ -146,9 +146,9 @@ public final class Permission { * See {@link #impliedBy}. * @throws IllegalStateException if this permission was already defined */ - public Permission(@Nonnull PermissionGroup group, @Nonnull String name, + public Permission(@NonNull PermissionGroup group, @NonNull String name, @CheckForNull Localizable description, @CheckForNull Permission impliedBy, boolean enable, - @Nonnull PermissionScope[] scopes) throws IllegalStateException { + @NonNull PermissionScope[] scopes) throws IllegalStateException { if(!JSONUtils.isJavaIdentifier(name)) throw new IllegalArgumentException(name+" is not a Java identifier"); this.owner = group.owner; @@ -164,8 +164,8 @@ public final class Permission { ALL.add(this); } - public Permission(@Nonnull PermissionGroup group, @Nonnull String name, - @CheckForNull Localizable description, @CheckForNull Permission impliedBy, @Nonnull PermissionScope scope) { + public Permission(@NonNull PermissionGroup group, @NonNull String name, + @CheckForNull Localizable description, @CheckForNull Permission impliedBy, @NonNull PermissionScope scope) { this(group,name,description,impliedBy,true,new PermissionScope[]{scope}); assert scope!=null; } @@ -175,7 +175,7 @@ public final class Permission { * Use {@link #Permission(PermissionGroup, String, Localizable, Permission, boolean, PermissionScope[])} */ @Deprecated - public Permission(@Nonnull PermissionGroup group, @Nonnull String name, @CheckForNull Localizable description, @CheckForNull Permission impliedBy, boolean enable) { + public Permission(@NonNull PermissionGroup group, @NonNull String name, @CheckForNull Localizable description, @CheckForNull Permission impliedBy, boolean enable) { this(group,name,description,impliedBy,enable,new PermissionScope[]{PermissionScope.JENKINS}); } @@ -184,7 +184,7 @@ public final class Permission { * Use {@link #Permission(PermissionGroup, String, Localizable, Permission, PermissionScope)} */ @Deprecated - public Permission(@Nonnull PermissionGroup group, @Nonnull String name, @CheckForNull Localizable description, @CheckForNull Permission impliedBy) { + public Permission(@NonNull PermissionGroup group, @NonNull String name, @CheckForNull Localizable description, @CheckForNull Permission impliedBy) { this(group, name, description, impliedBy, PermissionScope.JENKINS); } @@ -193,18 +193,18 @@ public final class Permission { * Use {@link #Permission(PermissionGroup, String, Localizable, Permission)} */ @Deprecated - public Permission(@Nonnull PermissionGroup group, @Nonnull String name, @CheckForNull Permission impliedBy) { + public Permission(@NonNull PermissionGroup group, @NonNull String name, @CheckForNull Permission impliedBy) { this(group,name,null,impliedBy); } - private Permission(@Nonnull PermissionGroup group, @Nonnull String name) { + private Permission(@NonNull PermissionGroup group, @NonNull String name) { this(group,name,null,null); } /** * Checks if this permission is contained in the specified scope, (either directly or indirectly.) */ - public boolean isContainedBy(@Nonnull PermissionScope s) { + public boolean isContainedBy(@NonNull PermissionScope s) { for (PermissionScope c : scopes) { if (c.isContainedBy(s)) return true; @@ -222,7 +222,7 @@ public final class Permission { * @return ID with the following format: permissionClass.permissionName * @see #fromId(String) */ - public @Nonnull String getId() { + public @NonNull String getId() { if (id == null) { return owner.getName() + '.' + name; } @@ -244,7 +244,7 @@ public final class Permission { * null if the conversion failed. * @see #getId() */ - public static @CheckForNull Permission fromId(@Nonnull String id) { + public static @CheckForNull Permission fromId(@NonNull String id) { int idx = id.lastIndexOf('.'); if(idx<0) return null; @@ -277,7 +277,7 @@ public final class Permission { * @return * always non-null. Read-only. */ - public static @Nonnull List getAll() { + public static @NonNull List getAll() { return ALL_VIEW; } diff --git a/core/src/main/java/hudson/security/PermissionGroup.java b/core/src/main/java/hudson/security/PermissionGroup.java index 891a3a5e0e..5ee861863f 100644 --- a/core/src/main/java/hudson/security/PermissionGroup.java +++ b/core/src/main/java/hudson/security/PermissionGroup.java @@ -30,8 +30,8 @@ import java.util.List; import java.util.Locale; import java.util.SortedSet; import java.util.TreeSet; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.jvnet.localizer.Localizable; @@ -43,7 +43,7 @@ import org.jvnet.localizer.Localizable; public final class PermissionGroup implements Iterable, Comparable { private final SortedSet permissions = new TreeSet<>(Permission.ID_COMPARATOR); - @Nonnull + @NonNull public final Class owner; /** @@ -60,7 +60,7 @@ public final class PermissionGroup implements Iterable, Comparable

, Comparable

SYSTEM user has full access"); @@ -65,7 +65,7 @@ public abstract class SidACL extends ACL { * true or false if {@link #hasPermission(Sid, Permission)} returns it. * Otherwise null, indicating that this ACL doesn't have any entry for it. */ - protected Boolean _hasPermission(@Nonnull Authentication a, Permission permission) { + protected Boolean _hasPermission(@NonNull Authentication a, Permission permission) { // ACL entries for this principal takes precedence Boolean b = hasPermission(new PrincipalSid(a),permission); if(LOGGER.isLoggable(FINER)) diff --git a/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java b/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java index 7c621658eb..6d2957cba9 100644 --- a/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java +++ b/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java @@ -41,8 +41,8 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -161,10 +161,15 @@ public class TokenBasedRememberMeServices2 extends TokenBasedRememberMeServices @Override public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) { - if(Jenkins.get().isDisableRememberMe()){ + Jenkins j = Jenkins.getInstanceOrNull(); + if (j == null) { + // as this filter could be called during restart, this corrects at least the symptoms + return null; + } + if (j.isDisableRememberMe()) { cancelCookie(request, response, null); return null; - }else { + } else { try { // we use a patched version of the super.autoLogin String rememberMeValue = findRememberMeCookieValue(request, response); @@ -283,7 +288,7 @@ public class TokenBasedRememberMeServices2 extends TokenBasedRememberMeServices /** * @return the decoded base64 of the cookie or {@code null} if the value was not correctly encoded */ - private @CheckForNull String decodeCookieBase64(@Nonnull String base64EncodedValue){ + private @CheckForNull String decodeCookieBase64(@NonNull String base64EncodedValue){ StringBuilder base64EncodedValueBuilder = new StringBuilder(base64EncodedValue); for (int j = 0; j < base64EncodedValueBuilder.length() % 4; j++) { base64EncodedValueBuilder.append("="); diff --git a/core/src/main/java/hudson/security/UnwrapSecurityExceptionFilter.java b/core/src/main/java/hudson/security/UnwrapSecurityExceptionFilter.java index a4d42ecd8c..8b428de6a4 100644 --- a/core/src/main/java/hudson/security/UnwrapSecurityExceptionFilter.java +++ b/core/src/main/java/hudson/security/UnwrapSecurityExceptionFilter.java @@ -51,7 +51,14 @@ public class UnwrapSecurityExceptionFilter implements Filter { chain.doFilter(request,response); } catch (ServletException e) { Throwable t = e.getRootCause(); - if (t instanceof JellyTagException) { + if (t != null && !(t instanceof JellyTagException)) { + if (t instanceof ServletException) { + t = ((ServletException) t).getRootCause(); + } else { + t = t.getCause(); + } + } + if (t != null && t instanceof JellyTagException) { JellyTagException jte = (JellyTagException) t; Throwable cause = jte.getCause(); if (cause instanceof AcegiSecurityException) { diff --git a/core/src/main/java/hudson/security/WhoAmI.java b/core/src/main/java/hudson/security/WhoAmI.java index 816e0a3f77..35f1e99b9e 100644 --- a/core/src/main/java/hudson/security/WhoAmI.java +++ b/core/src/main/java/hudson/security/WhoAmI.java @@ -24,7 +24,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Expose the data needed for /whoAmI, so it can be exposed by Api. @@ -70,7 +70,7 @@ public class WhoAmI implements UnprotectedRootAction { return auth().toString(); } - private @Nonnull Authentication auth() { + private @NonNull Authentication auth() { return Jenkins.getAuthentication(); } @@ -88,7 +88,7 @@ public class WhoAmI implements UnprotectedRootAction { // Used by Jelly @Restricted(NoExternalUse.class) - public boolean isHeaderDangerous(@Nonnull String name) { + public boolean isHeaderDangerous(@NonNull String name) { return dangerousHeaders.contains(name.toLowerCase(Locale.ENGLISH)); } diff --git a/core/src/main/java/hudson/security/csrf/CrumbFilter.java b/core/src/main/java/hudson/security/csrf/CrumbFilter.java index 160719894b..8d28351240 100644 --- a/core/src/main/java/hudson/security/csrf/CrumbFilter.java +++ b/core/src/main/java/hudson/security/csrf/CrumbFilter.java @@ -7,6 +7,7 @@ package hudson.security.csrf; import hudson.util.MultipartFormDataParser; import jenkins.model.Jenkins; +import jenkins.util.SystemProperties; import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; import org.kohsuke.MetaInfServices; import org.kohsuke.accmod.Restricted; @@ -15,7 +16,10 @@ import org.kohsuke.stapler.ForwardToView; import org.kohsuke.stapler.interceptor.RequirePOST; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Enumeration; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -26,6 +30,7 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; /** @@ -58,6 +63,54 @@ public class CrumbFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } + private static class Security1774ServletRequest extends HttpServletRequestWrapper { + public Security1774ServletRequest(HttpServletRequest request) { + super(request); + } + + @Override + public String getPathInfo() { + // see Stapler#getServletPath + return canonicalPath(getRequestURI().substring(getContextPath().length())); + } + + + // Copied from Stapler#canonicalPath + private static String canonicalPath(String path) { + List r = new ArrayList(Arrays.asList(path.split("/+"))); + for (int i=0; i0) { + r.remove(i-1); + i--; + } + } else { + i++; + } + } + + StringBuilder buf = new StringBuilder(); + if (path.startsWith("/")) + buf.append('/'); + boolean first = true; + for (String token : r) { + if (!first) buf.append('/'); + else first = false; + buf.append(token); + } + // translation: if (path.endsWith("/") && !buf.endsWith("/")) + if (path.endsWith("/") && (buf.length()==0 || buf.charAt(buf.length()-1)!='/')) + buf.append('/'); + return buf.toString(); + } + } + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { CrumbIssuer crumbIssuer = getCrumbIssuer(); if (crumbIssuer == null || !(request instanceof HttpServletRequest)) { @@ -69,8 +122,9 @@ public class CrumbFilter implements Filter { HttpServletResponse httpResponse = (HttpServletResponse) response; if ("POST".equals(httpRequest.getMethod())) { + HttpServletRequest wrappedRequest = UNPROCESSED_PATHINFO ? httpRequest : new Security1774ServletRequest(httpRequest); for (CrumbExclusion e : CrumbExclusion.all()) { - if (e.process(httpRequest,httpResponse,chain)) + if (e.process(wrappedRequest,httpResponse,chain)) return; } @@ -133,5 +187,7 @@ public class CrumbFilter implements Filter { public void destroy() { } + static /* non-final for Groovy */ boolean UNPROCESSED_PATHINFO = SystemProperties.getBoolean(CrumbFilter.class.getName() + ".UNPROCESSED_PATHINFO"); + private static final Logger LOGGER = Logger.getLogger(CrumbFilter.class.getName()); } diff --git a/core/src/main/java/hudson/security/csrf/GlobalCrumbIssuerConfiguration.java b/core/src/main/java/hudson/security/csrf/GlobalCrumbIssuerConfiguration.java index 914fba876d..3c44b6c73d 100644 --- a/core/src/main/java/hudson/security/csrf/GlobalCrumbIssuerConfiguration.java +++ b/core/src/main/java/hudson/security/csrf/GlobalCrumbIssuerConfiguration.java @@ -27,11 +27,14 @@ import hudson.Extension; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; import jenkins.model.Jenkins; +import jenkins.util.SystemProperties; import net.sf.json.JSONObject; import org.jenkinsci.Symbol; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Show the crumb configuration to the system config page. @@ -41,7 +44,7 @@ import javax.annotation.Nonnull; @Extension(ordinal=195) @Symbol("crumb") // immediately after the security setting public class GlobalCrumbIssuerConfiguration extends GlobalConfiguration { @Override - public @Nonnull GlobalConfigurationCategory getCategory() { + public @NonNull GlobalConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class); } @@ -49,14 +52,29 @@ public class GlobalCrumbIssuerConfiguration extends GlobalConfiguration { public boolean configure(StaplerRequest req, JSONObject json) throws FormException { // for compatibility reasons, the actual value is stored in Jenkins Jenkins j = Jenkins.get(); - if (json.has("csrf")) { - JSONObject csrf = json.getJSONObject("csrf"); - j.setCrumbIssuer(CrumbIssuer.all().newInstanceFromRadioList(csrf, "issuer")); + + if (json.has("crumbIssuer")) { + j.setCrumbIssuer(req.bindJSON(CrumbIssuer.class, json.getJSONObject("crumbIssuer"))); } else { - j.setCrumbIssuer(null); + j.setCrumbIssuer(createDefaultCrumbIssuer()); } return true; } -} + @Restricted(NoExternalUse.class) // Jelly + public CrumbIssuer getCrumbIssuer() { + return Jenkins.get().getCrumbIssuer(); + } + + @Restricted(NoExternalUse.class) + public static CrumbIssuer createDefaultCrumbIssuer() { + if (DISABLE_CSRF_PROTECTION) { + return null; + } + return new DefaultCrumbIssuer(SystemProperties.getBoolean(Jenkins.class.getName() + ".crumbIssuerProxyCompatibility", false)); + } + + @Restricted(NoExternalUse.class) + public static /* non-final */ boolean DISABLE_CSRF_PROTECTION = SystemProperties.getBoolean(GlobalCrumbIssuerConfiguration.class.getName() + ".DISABLE_CSRF_PROTECTION"); +} diff --git a/core/src/main/java/hudson/slaves/AbstractCloudComputer.java b/core/src/main/java/hudson/slaves/AbstractCloudComputer.java index 4dc3a71a72..51bb33d84c 100644 --- a/core/src/main/java/hudson/slaves/AbstractCloudComputer.java +++ b/core/src/main/java/hudson/slaves/AbstractCloudComputer.java @@ -30,7 +30,7 @@ import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.interceptor.RequirePOST; import java.io.IOException; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Partial implementation of {@link Computer} to be used in conjunction with diff --git a/core/src/main/java/hudson/slaves/AbstractCloudSlave.java b/core/src/main/java/hudson/slaves/AbstractCloudSlave.java index 3787d57c6f..228832297f 100644 --- a/core/src/main/java/hudson/slaves/AbstractCloudSlave.java +++ b/core/src/main/java/hudson/slaves/AbstractCloudSlave.java @@ -30,7 +30,7 @@ import hudson.model.Slave; import hudson.model.TaskListener; import hudson.util.StreamTaskListener; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.nio.charset.Charset; import java.util.List; @@ -45,7 +45,7 @@ import java.util.logging.Logger; */ public abstract class AbstractCloudSlave extends Slave { - public AbstractCloudSlave(@Nonnull String name, String remoteFS, ComputerLauncher launcher) + public AbstractCloudSlave(@NonNull String name, String remoteFS, ComputerLauncher launcher) throws FormException, IOException { super(name, remoteFS, launcher); } diff --git a/core/src/main/java/hudson/slaves/ChannelPinger.java b/core/src/main/java/hudson/slaves/ChannelPinger.java index d92ef1bff9..7aa5b9b5f7 100644 --- a/core/src/main/java/hudson/slaves/ChannelPinger.java +++ b/core/src/main/java/hudson/slaves/ChannelPinger.java @@ -37,7 +37,7 @@ import jenkins.slaves.PingFailureAnalyzer; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; diff --git a/core/src/main/java/hudson/slaves/Cloud.java b/core/src/main/java/hudson/slaves/Cloud.java index c1ef326612..9eeb4ad1d7 100644 --- a/core/src/main/java/hudson/slaves/Cloud.java +++ b/core/src/main/java/hudson/slaves/Cloud.java @@ -42,7 +42,7 @@ import hudson.security.Permission; import hudson.util.DescriptorList; import org.kohsuke.stapler.DataBoundConstructor; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collection; import java.util.concurrent.Future; @@ -112,11 +112,11 @@ public abstract class Cloud extends Actionable implements ExtensionPoint, Descri * @since 2.64 * @return Jenkins relative URL. */ - public @Nonnull String getUrl() { + public @NonNull String getUrl() { return "cloud/" + name; } - public @Nonnull String getSearchUrl() { + public @NonNull String getSearchUrl() { return getUrl(); } diff --git a/core/src/main/java/hudson/slaves/CloudProvisioningListener.java b/core/src/main/java/hudson/slaves/CloudProvisioningListener.java index 0c5d1bcbeb..a25433191e 100644 --- a/core/src/main/java/hudson/slaves/CloudProvisioningListener.java +++ b/core/src/main/java/hudson/slaves/CloudProvisioningListener.java @@ -10,7 +10,7 @@ import jenkins.model.Jenkins; import java.util.Collection; import java.util.concurrent.Future; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Allows extensions to be notified of events in any {@link Cloud} and to prevent @@ -73,7 +73,7 @@ public abstract class CloudProvisioningListener implements ExtensionPoint { * * @since 2.37 */ - public void onCommit(@Nonnull NodeProvisioner.PlannedNode plannedNode, @Nonnull Node node) { + public void onCommit(@NonNull NodeProvisioner.PlannedNode plannedNode, @NonNull Node node) { // Noop by default } @@ -96,8 +96,8 @@ public abstract class CloudProvisioningListener implements ExtensionPoint { * * @since 2.37 */ - public void onRollback(@Nonnull NodeProvisioner.PlannedNode plannedNode, @Nonnull Node node, - @Nonnull Throwable t) { + public void onRollback(@NonNull NodeProvisioner.PlannedNode plannedNode, @NonNull Node node, + @NonNull Throwable t) { // Noop by default } diff --git a/core/src/main/java/hudson/slaves/CloudRetentionStrategy.java b/core/src/main/java/hudson/slaves/CloudRetentionStrategy.java index 852f38d689..2e60f983d9 100644 --- a/core/src/main/java/hudson/slaves/CloudRetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/CloudRetentionStrategy.java @@ -25,7 +25,7 @@ package hudson.slaves; import jenkins.util.SystemProperties; -import javax.annotation.concurrent.GuardedBy; +import net.jcip.annotations.GuardedBy; import java.io.IOException; import java.util.logging.Logger; diff --git a/core/src/main/java/hudson/slaves/CloudSlaveRetentionStrategy.java b/core/src/main/java/hudson/slaves/CloudSlaveRetentionStrategy.java index e14d934801..c90667892e 100644 --- a/core/src/main/java/hudson/slaves/CloudSlaveRetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/CloudSlaveRetentionStrategy.java @@ -5,7 +5,7 @@ import hudson.model.Node; import java.util.concurrent.TimeUnit; import jenkins.model.Jenkins; -import javax.annotation.concurrent.GuardedBy; +import net.jcip.annotations.GuardedBy; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; diff --git a/core/src/main/java/hudson/slaves/ComputerConnector.java b/core/src/main/java/hudson/slaves/ComputerConnector.java index cc2ed12395..817f61d6f6 100644 --- a/core/src/main/java/hudson/slaves/ComputerConnector.java +++ b/core/src/main/java/hudson/slaves/ComputerConnector.java @@ -28,7 +28,7 @@ import hudson.model.AbstractDescribableImpl; import hudson.model.TaskListener; import java.io.IOException; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Factory of {@link ComputerLauncher}. @@ -49,7 +49,7 @@ public abstract class ComputerConnector extends AbstractDescribableImpl> applicableDescriptors(@CheckForNull Slave it, - @Nonnull Slave.SlaveDescriptor itDescriptor) { + @NonNull Slave.SlaveDescriptor itDescriptor) { List> r = new ArrayList<>(); for (Descriptor d : itDescriptor.computerLauncherDescriptors(it)) { if (DelegatingComputerLauncher.class.isAssignableFrom(d.getKlass().toJavaClass())) continue; diff --git a/core/src/main/java/hudson/slaves/DumbSlave.java b/core/src/main/java/hudson/slaves/DumbSlave.java index af1cc47dc1..153477c8ac 100644 --- a/core/src/main/java/hudson/slaves/DumbSlave.java +++ b/core/src/main/java/hudson/slaves/DumbSlave.java @@ -34,7 +34,7 @@ import java.util.List; import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Default {@link Slave} implementation for computers that do not belong to a higher level structure, @@ -62,7 +62,7 @@ public final class DumbSlave extends Slave { } @DataBoundConstructor - public DumbSlave(@Nonnull String name, String remoteFS, ComputerLauncher launcher) throws FormException, IOException { + public DumbSlave(@NonNull String name, String remoteFS, ComputerLauncher launcher) throws FormException, IOException { super(name, remoteFS, launcher); } diff --git a/core/src/main/java/hudson/slaves/JNLPLauncher.java b/core/src/main/java/hudson/slaves/JNLPLauncher.java index db006ce8bd..c15d125b5e 100644 --- a/core/src/main/java/hudson/slaves/JNLPLauncher.java +++ b/core/src/main/java/hudson/slaves/JNLPLauncher.java @@ -29,8 +29,8 @@ import hudson.model.Computer; import hudson.model.Descriptor; import hudson.model.TaskListener; import hudson.util.FormValidation; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; import jenkins.slaves.RemotingWorkDirSettings; @@ -71,7 +71,7 @@ public class JNLPLauncher extends ComputerLauncher { @CheckForNull public final String vmargs; - @Nonnull + @NonNull private RemotingWorkDirSettings workDirSettings = RemotingWorkDirSettings.getEnabledDefaults(); private boolean webSocket; @@ -132,13 +132,13 @@ public class JNLPLauncher extends ComputerLauncher { * * @since 2.72 */ - @Nonnull + @NonNull public RemotingWorkDirSettings getWorkDirSettings() { return workDirSettings; } @DataBoundSetter - public final void setWorkDirSettings(@Nonnull RemotingWorkDirSettings workDirSettings) { + public final void setWorkDirSettings(@NonNull RemotingWorkDirSettings workDirSettings) { this.workDirSettings = workDirSettings; } @@ -148,14 +148,14 @@ public class JNLPLauncher extends ComputerLauncher { } /** - * @since TODO + * @since 2.216 */ public boolean isWebSocket() { return webSocket; } /** - * @since TODO + * @since 2.216 */ @DataBoundSetter public void setWebSocket(boolean webSocket) { @@ -181,9 +181,9 @@ public class JNLPLauncher extends ComputerLauncher { * @param computer Computer * @return Command line options for launching with the WorkDir */ - @Nonnull + @NonNull @Restricted(NoExternalUse.class) - public String getWorkDirOptions(@Nonnull Computer computer) { + public String getWorkDirOptions(@NonNull Computer computer) { if(!(computer instanceof SlaveComputer)) { return ""; } diff --git a/core/src/main/java/hudson/slaves/NodeList.java b/core/src/main/java/hudson/slaves/NodeList.java index 87f7a82328..30e842c600 100644 --- a/core/src/main/java/hudson/slaves/NodeList.java +++ b/core/src/main/java/hudson/slaves/NodeList.java @@ -39,7 +39,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * {@link CopyOnWriteArrayList} for {@link Node} that has special serialization semantics diff --git a/core/src/main/java/hudson/slaves/NodeProperty.java b/core/src/main/java/hudson/slaves/NodeProperty.java index a24b343b7b..324b089f43 100644 --- a/core/src/main/java/hudson/slaves/NodeProperty.java +++ b/core/src/main/java/hudson/slaves/NodeProperty.java @@ -47,7 +47,7 @@ import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Extensible property of {@link Node}. @@ -166,7 +166,7 @@ public abstract class NodeProperty implements ReconfigurableDesc * * @since 1.489 */ - public void buildEnvVars(@Nonnull EnvVars env, @Nonnull TaskListener listener) throws IOException,InterruptedException { + public void buildEnvVars(@NonNull EnvVars env, @NonNull TaskListener listener) throws IOException,InterruptedException { // default is no-op } diff --git a/core/src/main/java/hudson/slaves/NodeProvisioner.java b/core/src/main/java/hudson/slaves/NodeProvisioner.java index acabe894ba..2a03bad8e3 100644 --- a/core/src/main/java/hudson/slaves/NodeProvisioner.java +++ b/core/src/main/java/hudson/slaves/NodeProvisioner.java @@ -35,9 +35,9 @@ import jenkins.util.SystemProperties; import jenkins.util.Timer; import org.jenkinsci.Symbol; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.GuardedBy; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.GuardedBy; import java.awt.Color; import java.util.Arrays; import java.util.Collections; @@ -382,9 +382,9 @@ public class NodeProvisioner { * @param state the current state. * @return the decision. */ - @Nonnull + @NonNull @GuardedBy("NodeProvisioner.this") - public abstract StrategyDecision apply(@Nonnull StrategyState state); + public abstract StrategyDecision apply(@NonNull StrategyState state); } @@ -631,9 +631,9 @@ public class NodeProvisioner { @Extension @Symbol("standard") public static class StandardStrategyImpl extends Strategy { - @Nonnull + @NonNull @Override - public StrategyDecision apply(@Nonnull StrategyState state) { + public StrategyDecision apply(@NonNull StrategyState state) { /* Here we determine how many additional agents we need to keep up with the load (if at all), which involves a simple math. diff --git a/core/src/main/java/hudson/slaves/NodeSpecific.java b/core/src/main/java/hudson/slaves/NodeSpecific.java index e40ef509df..a59230cf92 100644 --- a/core/src/main/java/hudson/slaves/NodeSpecific.java +++ b/core/src/main/java/hudson/slaves/NodeSpecific.java @@ -28,7 +28,7 @@ import hudson.model.Node; import hudson.model.EnvironmentSpecific; import hudson.model.TaskListener; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; /** @@ -46,5 +46,5 @@ public interface NodeSpecific> { /** * Returns a specialized copy of T for functioning in the given node. */ - T forNode(@Nonnull Node node, TaskListener log) throws IOException, InterruptedException; + T forNode(@NonNull Node node, TaskListener log) throws IOException, InterruptedException; } diff --git a/core/src/main/java/hudson/slaves/OfflineCause.java b/core/src/main/java/hudson/slaves/OfflineCause.java index a158ce2b8c..cf99017399 100644 --- a/core/src/main/java/hudson/slaves/OfflineCause.java +++ b/core/src/main/java/hudson/slaves/OfflineCause.java @@ -33,8 +33,8 @@ import org.jvnet.localizer.Localizable; import org.kohsuke.stapler.export.ExportedBean; import org.kohsuke.stapler.export.Exported; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.ObjectStreamException; import java.util.Collections; import java.util.Date; @@ -70,7 +70,7 @@ public abstract class OfflineCause { * * @since 1.612 */ - public final @Nonnull Date getTime() { + public final @NonNull Date getTime() { return new Date(timestamp); } diff --git a/core/src/main/java/hudson/slaves/RetentionStrategy.java b/core/src/main/java/hudson/slaves/RetentionStrategy.java index 6cc0787afb..14d8631de7 100644 --- a/core/src/main/java/hudson/slaves/RetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/RetentionStrategy.java @@ -35,11 +35,11 @@ import jenkins.model.Jenkins; import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; -import javax.annotation.concurrent.GuardedBy; +import net.jcip.annotations.GuardedBy; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Controls when to take {@link Computer} offline, bring it back online, or even to destroy it. @@ -58,7 +58,7 @@ public abstract class RetentionStrategy extends AbstractDesc * rechecked earlier or later than this! */ @GuardedBy("hudson.model.Queue.lock") - public abstract long check(@Nonnull T c); + public abstract long check(@NonNull T c); /** * This method is called to determine whether manual launching of the agent is allowed right now. @@ -96,7 +96,7 @@ public abstract class RetentionStrategy extends AbstractDesc * @param c Computer instance * @since 1.275 */ - public void start(final @Nonnull T c) { + public void start(final @NonNull T c) { Queue.withLock((Runnable) () -> check(c)); } diff --git a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java index df9ff8ce07..ea7b921131 100644 --- a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java @@ -35,7 +35,7 @@ import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; -import javax.annotation.concurrent.GuardedBy; +import net.jcip.annotations.GuardedBy; import java.io.InvalidObjectException; import java.io.ObjectStreamException; import java.util.Calendar; diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index 4c59ec8aae..cc1f5cdaa9 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -54,6 +54,7 @@ import hudson.util.StreamTaskListener; import hudson.util.VersionNumber; import hudson.util.io.RewindableFileOutputStream; import hudson.util.io.RewindableRotatingFileOutputStream; +import jenkins.agents.AgentComputerUtil; import jenkins.model.Jenkins; import jenkins.security.ChannelConfigurator; import jenkins.security.MasterToSlaveCallable; @@ -75,10 +76,10 @@ import org.kohsuke.stapler.WebMethod; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.interceptor.RequirePOST; -import javax.annotation.CheckForNull; -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; -import javax.annotation.OverridingMethodsMustInvokeSuper; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckReturnValue; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.OverrideMustInvoke; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -92,10 +93,13 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.Future; +import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; +import java.util.stream.Stream; import static hudson.slaves.SlaveComputer.LogHolder.SLAVE_LOG_HANDLER; import org.jenkinsci.remoting.util.LoggingChannelListener; @@ -171,7 +175,7 @@ public class SlaveComputer extends Computer { } @Override - @OverridingMethodsMustInvokeSuper + @OverrideMustInvoke public boolean isAcceptingTasks() { // our boolean flag is an override on any additional programmatic reasons why this agent might not be // accepting tasks. @@ -380,8 +384,8 @@ public class SlaveComputer extends Computer { * Same as {@link #setChannel(InputStream, OutputStream, OutputStream, Channel.Listener)}, but for * {@link TaskListener}. */ - public void setChannel(@Nonnull InputStream in, @Nonnull OutputStream out, - @Nonnull TaskListener taskListener, + public void setChannel(@NonNull InputStream in, @NonNull OutputStream out, + @NonNull TaskListener taskListener, @CheckForNull Channel.Listener listener) throws IOException, InterruptedException { setChannel(in,out,taskListener.getLogger(),listener); } @@ -405,7 +409,7 @@ public class SlaveComputer extends Computer { * By the time this method is called, the cause of the termination is reported to the user, * so the implementation of the listener doesn't need to do that again. */ - public void setChannel(@Nonnull InputStream in, @Nonnull OutputStream out, + public void setChannel(@NonNull InputStream in, @NonNull OutputStream out, @CheckForNull OutputStream launchLog, @CheckForNull Channel.Listener listener) throws IOException, InterruptedException { ChannelBuilder cb = new ChannelBuilder(nodeName,threadPoolForRemoting) @@ -437,8 +441,8 @@ public class SlaveComputer extends Computer { * @since 2.127 */ @Restricted(Beta.class) - public void setChannel(@Nonnull ChannelBuilder cb, - @Nonnull CommandTransport commandTransport, + public void setChannel(@NonNull ChannelBuilder cb, + @NonNull CommandTransport commandTransport, @CheckForNull Channel.Listener listener) throws IOException, InterruptedException { for (ChannelConfigurator cc : ChannelConfigurator.all()) { cc.onChannelBuilding(cb,this); @@ -599,7 +603,7 @@ public class SlaveComputer extends Computer { * @param listener Channel event listener to be attached (if not {@code null}) * @since 1.444 */ - public void setChannel(@Nonnull Channel channel, + public void setChannel(@NonNull Channel channel, @CheckForNull OutputStream launchLog, @CheckForNull Channel.Listener listener) throws IOException, InterruptedException { if(this.channel!=null) @@ -1015,7 +1019,18 @@ public class SlaveComputer extends Computer { } public Void call() { - SLAVE_LOG_HANDLER = new RingBufferLogHandler(ringBufferSize); + SLAVE_LOG_HANDLER = new RingBufferLogHandler(ringBufferSize) { + Formatter dummy = new SimpleFormatter(); + @Override + public synchronized void publish(LogRecord record) { + // see LogRecord.writeObject for dangers of serializing non-String/null parameters + if (record.getMessage() != null && record.getParameters() != null && Stream.of(record.getParameters()).anyMatch(p -> p != null && !(p instanceof String))) { + record.setMessage(dummy.formatMessage(record)); + record.setParameters(null); + } + super.publish(record); + } + }; // avoid double installation of the handler. Inbound agents can reconnect to the master multiple times // and each connection gets a different RemoteClassLoader, so we need to evict them by class name, @@ -1053,17 +1068,11 @@ public class SlaveComputer extends Computer { * * @return null if the calling thread doesn't have any trace of where its master is. * @since 1.362 + * @deprecated Use {@link AgentComputerUtil#getChannelToMaster()} instead. */ + @Deprecated public static VirtualChannel getChannelToMaster() { - if (Jenkins.getInstanceOrNull()!=null) // check if calling thread is on master or on slave - return FilePath.localChannel; - - // if this method is called from within the agent computation thread, this should work - Channel c = Channel.current(); - if (c!=null && Boolean.TRUE.equals(c.getProperty("slave"))) - return c; - - return null; + return AgentComputerUtil.getChannelToMaster(); } /** diff --git a/core/src/main/java/hudson/slaves/WorkspaceList.java b/core/src/main/java/hudson/slaves/WorkspaceList.java index bf52303139..8afd10b414 100644 --- a/core/src/main/java/hudson/slaves/WorkspaceList.java +++ b/core/src/main/java/hudson/slaves/WorkspaceList.java @@ -36,7 +36,8 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Used by {@link Computer} to keep track of workspaces that are actively in use. @@ -77,7 +78,7 @@ public final class WorkspaceList { */ public final boolean quick; - public final @Nonnull FilePath path; + public final @NonNull FilePath path; /** * Multiple threads can acquire the same lock if they share the same context object. @@ -86,11 +87,11 @@ public final class WorkspaceList { public int lockCount=1; - private Entry(@Nonnull FilePath path, boolean quick) { + private Entry(@NonNull FilePath path, boolean quick) { this(path,quick,new Object()); // unique context } - private Entry(@Nonnull FilePath path, boolean quick, Object context) { + private Entry(@NonNull FilePath path, boolean quick, Object context) { this.path = path; this.quick = quick; this.context = context; @@ -109,9 +110,9 @@ public final class WorkspaceList { * Represents a leased workspace that needs to be returned later. */ public static abstract class Lease implements /*Auto*/Closeable { - public final @Nonnull FilePath path; + public final @NonNull FilePath path; - protected Lease(@Nonnull FilePath path) { + protected Lease(@NonNull FilePath path) { if (path == null) { // Protection from old API throw new NullPointerException("The specified FilePath is null"); } @@ -134,7 +135,7 @@ public final class WorkspaceList { /** * Creates a dummy {@link Lease} object that does no-op in the release. */ - public static Lease createDummyLease(@Nonnull FilePath p) { + public static Lease createDummyLease(@NonNull FilePath p) { return new Lease(p) { public void release() { // noop @@ -146,7 +147,7 @@ public final class WorkspaceList { * Creates a {@link Lease} object that points to the specified path, but the lock * is controlled by the given parent lease object. */ - public static Lease createLinkedDummyLease(@Nonnull FilePath p, final Lease parent) { + public static Lease createLinkedDummyLease(@NonNull FilePath p, final Lease parent) { return new Lease(p) { public void release() { parent.release(); @@ -167,7 +168,7 @@ public final class WorkspaceList { * This method doesn't block prolonged amount of time. Whenever a desired workspace * is in use, the unique variation is added. */ - public synchronized Lease allocate(@Nonnull FilePath base) throws InterruptedException { + public synchronized Lease allocate(@NonNull FilePath base) throws InterruptedException { return allocate(base,new Object()); } @@ -178,7 +179,7 @@ public final class WorkspaceList { * Threads that share the same context can re-acquire the same lock (which will just increment the lock count.) * This allows related executors to share the same workspace. */ - public synchronized Lease allocate(@Nonnull FilePath base, Object context) throws InterruptedException { + public synchronized Lease allocate(@NonNull FilePath base, Object context) throws InterruptedException { for (int i=1; ; i++) { FilePath candidate = i==1 ? base : base.withSuffix(COMBINATOR+i); Entry e = inUse.get(candidate.getRemote()); @@ -191,7 +192,7 @@ public final class WorkspaceList { /** * Just record that this workspace is being used, without paying any attention to the synchronization support. */ - public synchronized Lease record(@Nonnull FilePath p) { + public synchronized Lease record(@NonNull FilePath p) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "recorded " + p, new Throwable("from " + this)); } @@ -204,7 +205,7 @@ public final class WorkspaceList { /** * Releases an allocated or acquired workspace. */ - private synchronized void _release(@Nonnull FilePath p) { + private synchronized void _release(@NonNull FilePath p) { Entry old = inUse.get(p.getRemote()); if (old==null) throw new AssertionError("Releasing unallocated workspace "+p); @@ -223,7 +224,7 @@ public final class WorkspaceList { * @return * The same {@link FilePath} as given to this method. */ - public synchronized Lease acquire(@Nonnull FilePath p) throws InterruptedException { + public synchronized Lease acquire(@NonNull FilePath p) throws InterruptedException { return acquire(p,false); } @@ -234,7 +235,7 @@ public final class WorkspaceList { * If true, indicates that the acquired workspace will be returned quickly. * This makes other calls to {@link #allocate(FilePath)} to wait for the release of this workspace. */ - public synchronized Lease acquire(@Nonnull FilePath p, boolean quick) throws InterruptedException { + public synchronized Lease acquire(@NonNull FilePath p, boolean quick) throws InterruptedException { return acquire(p,quick,new Object()); } @@ -245,7 +246,7 @@ public final class WorkspaceList { * Threads that share the same context can re-acquire the same lock (which will just increment the lock count.) * This allows related executors to share the same workspace. */ - public synchronized Lease acquire(@Nonnull FilePath p, boolean quick, Object context) throws InterruptedException { + public synchronized Lease acquire(@NonNull FilePath p, boolean quick, Object context) throws InterruptedException { Entry e; Thread t = Thread.currentThread(); @@ -273,7 +274,7 @@ public final class WorkspaceList { /** * Wraps a path into a valid lease. */ - private Lease lease(@Nonnull FilePath p) { + private Lease lease(@NonNull FilePath p) { return new Lease(p) { final AtomicBoolean released = new AtomicBoolean(); public void release() { @@ -299,9 +300,10 @@ public final class WorkspaceList { *

The resulting directory may not exist; you may call {@link FilePath#mkdirs} on it if you need it to. * It may be deleted alongside the workspace itself during cleanup actions. * @param ws a directory such as a build workspace - * @return a sibling directory, for example {@code …/something@tmp} for {@code …/something} + * @return a sibling directory, for example {@code …/something@tmp} for {@code …/something}, or {@code null} if {@link FilePath#getParent} is null * @since 1.652 */ + @CheckForNull public static FilePath tempDir(FilePath ws) { return ws.sibling(ws.getName() + COMBINATOR + "tmp"); } diff --git a/core/src/main/java/hudson/tasks/ArtifactArchiver.java b/core/src/main/java/hudson/tasks/ArtifactArchiver.java index dc50c25501..c379d69bd0 100644 --- a/core/src/main/java/hudson/tasks/ArtifactArchiver.java +++ b/core/src/main/java/hudson/tasks/ArtifactArchiver.java @@ -53,10 +53,10 @@ import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import net.sf.json.JSONObject; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.BuildDiscarder; import jenkins.model.Jenkins; import jenkins.tasks.SimpleBuildStep; @@ -88,7 +88,7 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep { /** * Fail (or not) the build if archiving returns nothing. */ - @Nonnull + @NonNull private Boolean allowEmptyArchive; /** @@ -101,15 +101,21 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep { /** * Default ant exclusion */ - @Nonnull + @NonNull private Boolean defaultExcludes = true; /** * Indicate whether include and exclude patterns should be considered as case sensitive */ - @Nonnull + @NonNull private Boolean caseSensitive = true; + /** + * Indicate whether symbolic links should be followed or not + */ + @NonNull + private Boolean followSymlinks = true; + @DataBoundConstructor public ArtifactArchiver(String artifacts) { this.artifacts = artifacts.trim(); allowEmptyArchive = false; @@ -153,6 +159,9 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep { if (caseSensitive == null) { caseSensitive = true; } + if (followSymlinks == null) { + followSymlinks = true; + } return this; } @@ -214,6 +223,14 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep { this.caseSensitive = caseSensitive; } + public boolean isFollowSymlinks() { + return followSymlinks; + } + + @DataBoundSetter public final void setFollowSymlinks(boolean followSymlinks) { + this.followSymlinks = followSymlinks; + } + @Override public void perform(Run build, FilePath ws, Launcher launcher, TaskListener listener) throws IOException, InterruptedException { if(artifacts.length()==0) { @@ -230,7 +247,7 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep { try { String artifacts = build.getEnvironment(listener).expand(this.artifacts); - Map files = ws.act(new ListFiles(artifacts, excludes, defaultExcludes, caseSensitive)); + Map files = ws.act(new ListFiles(artifacts, excludes, defaultExcludes, caseSensitive, followSymlinks)); if (!files.isEmpty()) { build.pickArtifactManager().archive(ws, launcher, BuildListenerAdapter.wrap(listener), files); if (fingerprint) { @@ -275,12 +292,14 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep { private final String includes, excludes; private final boolean defaultExcludes; private final boolean caseSensitive; + private final boolean followSymlinks; - ListFiles(String includes, String excludes, boolean defaultExcludes, boolean caseSensitive) { + ListFiles(String includes, String excludes, boolean defaultExcludes, boolean caseSensitive, boolean followSymlinks) { this.includes = includes; this.excludes = excludes; this.defaultExcludes = defaultExcludes; this.caseSensitive = caseSensitive; + this.followSymlinks = followSymlinks; } @Override public Map invoke(File basedir, VirtualChannel channel) throws IOException, InterruptedException { @@ -289,6 +308,7 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep { FileSet fileSet = Util.createFileSet(basedir, includes, excludes); fileSet.setDefaultexcludes(defaultExcludes); fileSet.setCaseSensitive(caseSensitive); + fileSet.setFollowSymlinks(followSymlinks); for (String f : fileSet.getDirectoryScanner().getIncludedFiles()) { f = f.replace(File.separatorChar, '/'); diff --git a/core/src/main/java/hudson/tasks/BatchFile.java b/core/src/main/java/hudson/tasks/BatchFile.java index f390b0880d..4174820236 100644 --- a/core/src/main/java/hudson/tasks/BatchFile.java +++ b/core/src/main/java/hudson/tasks/BatchFile.java @@ -38,7 +38,7 @@ import org.kohsuke.stapler.QueryParameter; import java.io.ObjectStreamException; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Executes commands by using Windows batch file. diff --git a/core/src/main/java/hudson/tasks/BuildStep.java b/core/src/main/java/hudson/tasks/BuildStep.java index 600b419a1a..2af5f84c05 100644 --- a/core/src/main/java/hudson/tasks/BuildStep.java +++ b/core/src/main/java/hudson/tasks/BuildStep.java @@ -49,7 +49,7 @@ import java.util.WeakHashMap; import jenkins.security.QueueItemAuthenticator; import org.acegisecurity.Authentication; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; /** @@ -157,7 +157,7 @@ public interface BuildStep { * @return * can be empty but never null. */ - @Nonnull + @NonNull Collection getProjectActions(AbstractProject project); diff --git a/core/src/main/java/hudson/tasks/BuildStepCompatibilityLayer.java b/core/src/main/java/hudson/tasks/BuildStepCompatibilityLayer.java index dadcc90a81..7d7c6422d4 100644 --- a/core/src/main/java/hudson/tasks/BuildStepCompatibilityLayer.java +++ b/core/src/main/java/hudson/tasks/BuildStepCompatibilityLayer.java @@ -42,7 +42,7 @@ import hudson.model.Run; import hudson.model.TaskListener; import jenkins.tasks.SimpleBuildStep; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Provides compatibility with {@link BuildStep} before 1.150 @@ -92,7 +92,7 @@ public abstract class BuildStepCompatibilityLayer implements BuildStep { return null; } - @Nonnull + @NonNull public Collection getProjectActions(AbstractProject project) { // delegate to getJobAction (singular) for backward compatible behavior Action a = getProjectAction(project); diff --git a/core/src/main/java/hudson/tasks/BuildTrigger.java b/core/src/main/java/hudson/tasks/BuildTrigger.java index aad8f36a44..aaa32b12b6 100644 --- a/core/src/main/java/hudson/tasks/BuildTrigger.java +++ b/core/src/main/java/hudson/tasks/BuildTrigger.java @@ -57,8 +57,8 @@ import java.util.List; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.DependencyDeclarer; import jenkins.model.Jenkins; import jenkins.model.ParameterizedJobMixIn; @@ -160,8 +160,8 @@ public class BuildTrigger extends Recorder implements DependencyDeclarer { } @SuppressWarnings("unchecked") - @Nonnull - public List> getChildJobs(@Nonnull AbstractProject owner) { + @NonNull + public List> getChildJobs(@NonNull AbstractProject owner) { return Items.fromNameList(owner.getParent(), childProjects, (Class>) (Class) Job.class); } diff --git a/core/src/main/java/hudson/tasks/CommandInterpreter.java b/core/src/main/java/hudson/tasks/CommandInterpreter.java index 2b7908fdfa..64160f2915 100644 --- a/core/src/main/java/hudson/tasks/CommandInterpreter.java +++ b/core/src/main/java/hudson/tasks/CommandInterpreter.java @@ -40,7 +40,7 @@ import java.io.IOException; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Common part between {@link Shell} and {@link BatchFile}. @@ -158,7 +158,7 @@ public abstract class CommandInterpreter extends Builder { /** * Creates a script file in a temporary name in the specified directory. */ - public FilePath createScriptFile(@Nonnull FilePath dir) throws IOException, InterruptedException { + public FilePath createScriptFile(@NonNull FilePath dir) throws IOException, InterruptedException { return dir.createTextTempFile("jenkins", getFileExtension(), getContents(), false); } diff --git a/core/src/main/java/hudson/tasks/Maven.java b/core/src/main/java/hudson/tasks/Maven.java index 1c17e767ea..438c513562 100644 --- a/core/src/main/java/hudson/tasks/Maven.java +++ b/core/src/main/java/hudson/tasks/Maven.java @@ -69,7 +69,7 @@ import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.File; import java.io.IOException; import java.io.ObjectStreamException; @@ -150,7 +150,7 @@ public class Maven extends Builder { * * @since 2.12 */ - private @Nonnull Boolean injectBuildVariables; + private @NonNull Boolean injectBuildVariables; private final static String MAVEN_1_INSTALLATION_COMMON_FILE = "bin/maven"; private final static String MAVEN_2_INSTALLATION_COMMON_FILE = "bin/mvn"; diff --git a/core/src/main/java/hudson/tasks/Shell.java b/core/src/main/java/hudson/tasks/Shell.java index 571a68c439..1aa1502f04 100644 --- a/core/src/main/java/hudson/tasks/Shell.java +++ b/core/src/main/java/hudson/tasks/Shell.java @@ -50,7 +50,7 @@ import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Executes a series of commands by using a shell. diff --git a/core/src/main/java/hudson/tasks/UserAvatarResolver.java b/core/src/main/java/hudson/tasks/UserAvatarResolver.java index 491f9d29ad..b022c7a126 100644 --- a/core/src/main/java/hudson/tasks/UserAvatarResolver.java +++ b/core/src/main/java/hudson/tasks/UserAvatarResolver.java @@ -33,7 +33,7 @@ import hudson.ExtensionList; import hudson.ExtensionPoint; import hudson.Functions; import hudson.model.User; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Infers avatar image URLs for users diff --git a/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java b/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java index 6fa7dd8371..5867421d79 100644 --- a/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java +++ b/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java @@ -157,10 +157,12 @@ public abstract class DownloadFromUrlInstaller extends ToolInstaller { if (toolInstallerList != null) { ToolInstallerEntry[] entryList = toolInstallerList.list; - ToolInstallerEntry sampleEntry = entryList[0]; - if (sampleEntry != null) { - if (sampleEntry.id != null && sampleEntry.name != null && sampleEntry.url != null) { - return true; + if (entryList != null) { + ToolInstallerEntry sampleEntry = entryList[0]; + if (sampleEntry != null) { + if (sampleEntry.id != null && sampleEntry.name != null && sampleEntry.url != null) { + return true; + } } } } diff --git a/core/src/main/java/hudson/tools/ToolDescriptor.java b/core/src/main/java/hudson/tools/ToolDescriptor.java index fd9d4f6443..2ea93b2a73 100644 --- a/core/src/main/java/hudson/tools/ToolDescriptor.java +++ b/core/src/main/java/hudson/tools/ToolDescriptor.java @@ -44,7 +44,7 @@ import org.jvnet.tiger_types.Types; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * {@link Descriptor} for {@link ToolInstallation}. @@ -116,7 +116,7 @@ public abstract class ToolDescriptor extends Descrip @Override - public @Nonnull GlobalConfigurationCategory getCategory() { + public @NonNull GlobalConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(ToolConfigurationCategory.class); } diff --git a/core/src/main/java/hudson/tools/ToolInstallation.java b/core/src/main/java/hudson/tools/ToolInstallation.java index 85387795c0..df7e30f69f 100644 --- a/core/src/main/java/hudson/tools/ToolInstallation.java +++ b/core/src/main/java/hudson/tools/ToolInstallation.java @@ -40,8 +40,8 @@ import java.util.List; import com.thoughtworks.xstream.annotations.XStreamSerializable; import com.thoughtworks.xstream.converters.UnmarshallingContext; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; /** @@ -165,7 +165,7 @@ public abstract class ToolInstallation extends AbstractDescribableImpl { if (actions == null) { additionalActions = new Action[0]; } else { - additionalActions = actions; + additionalActions = Arrays.copyOf(actions, actions.length); } } diff --git a/core/src/main/java/hudson/triggers/SafeTimerTask.java b/core/src/main/java/hudson/triggers/SafeTimerTask.java index 73409e4ae4..bda2fe1657 100644 --- a/core/src/main/java/hudson/triggers/SafeTimerTask.java +++ b/core/src/main/java/hudson/triggers/SafeTimerTask.java @@ -52,7 +52,7 @@ public abstract class SafeTimerTask extends TimerTask { /** * Lambda-friendly means of creating a task. - * @since TODO + * @since 2.216 */ public static SafeTimerTask of(ExceptionRunnable r) { return new SafeTimerTask() { @@ -64,7 +64,7 @@ public abstract class SafeTimerTask extends TimerTask { } /** * @see #of - * @since TODO + * @since 2.216 */ @FunctionalInterface public interface ExceptionRunnable { diff --git a/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java b/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java index 131181c0be..42ae6c88cc 100644 --- a/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java +++ b/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java @@ -11,7 +11,7 @@ import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.interceptor.RequirePOST; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -25,15 +25,15 @@ import java.util.logging.Logger; @Extension public class SlowTriggerAdminMonitor extends AdministrativeMonitor { - @Nonnull + @NonNull private final Map errors = new ConcurrentHashMap<>(); public static int MAX_ENTRIES = 10; - @Nonnull + @NonNull private static final Logger LOGGER = Logger.getLogger(SlowTriggerAdminMonitor.class.getName()); - @Nonnull + @NonNull public static SlowTriggerAdminMonitor getInstance() { return ExtensionList.lookup(SlowTriggerAdminMonitor.class).get(0); } @@ -47,7 +47,7 @@ public class SlowTriggerAdminMonitor extends AdministrativeMonitor { } @Override - @Nonnull + @NonNull public String getDisplayName() { return Messages.SlowTriggerAdminMonitor_DisplayName(); } @@ -58,7 +58,7 @@ public class SlowTriggerAdminMonitor extends AdministrativeMonitor { } } - public void report(@Nonnull final String trigger, @Nonnull final String msg) { + public void report(@NonNull final String trigger, @NonNull final String msg) { synchronized (errors) { if (errors.size() >= MAX_ENTRIES && !errors.containsKey(trigger)) { @@ -78,14 +78,14 @@ public class SlowTriggerAdminMonitor extends AdministrativeMonitor { errors.put(trigger, new Value(msg)); } - @Nonnull + @NonNull public Map getErrors() { return new HashMap<>(errors); } @Restricted(DoNotUse.class) @RequirePOST - @Nonnull + @NonNull public HttpResponse doClear() throws IOException { Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); clear(); @@ -97,22 +97,22 @@ public class SlowTriggerAdminMonitor extends AdministrativeMonitor { private final LocalDateTime time; private final String msg; - Value(@Nonnull String msg) { + Value(@NonNull String msg) { this.msg = msg; this.time = LocalDateTime.now(); } - @Nonnull + @NonNull public String getTime() { return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).format(time); } - @Nonnull + @NonNull protected LocalDateTime getTimeLDT() { return time; } - @Nonnull + @NonNull public String getMsg() { return msg; } diff --git a/core/src/main/java/hudson/triggers/TimerTrigger.java b/core/src/main/java/hudson/triggers/TimerTrigger.java index da844fa6b6..5085caf1d2 100644 --- a/core/src/main/java/hudson/triggers/TimerTrigger.java +++ b/core/src/main/java/hudson/triggers/TimerTrigger.java @@ -44,7 +44,7 @@ import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * {@link Trigger} that runs a job periodically. @@ -54,7 +54,7 @@ import javax.annotation.Nonnull; public class TimerTrigger extends Trigger { @DataBoundConstructor - public TimerTrigger(@Nonnull String spec) throws ANTLRException { + public TimerTrigger(@NonNull String spec) throws ANTLRException { super(spec); } diff --git a/core/src/main/java/hudson/triggers/Trigger.java b/core/src/main/java/hudson/triggers/Trigger.java index ae7ad4bba4..647d445a41 100644 --- a/core/src/main/java/hudson/triggers/Trigger.java +++ b/core/src/main/java/hudson/triggers/Trigger.java @@ -58,8 +58,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import antlr.ANTLRException; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.model.Items; import jenkins.model.ParameterizedJobMixIn; @@ -165,7 +165,7 @@ public abstract class Trigger implements Describable> * periodically. This is useful when your trigger does * some polling work. */ - protected Trigger(@Nonnull String cronTabSpec) throws ANTLRException { + protected Trigger(@NonNull String cronTabSpec) throws ANTLRException { this.spec = cronTabSpec; this.tabs = CronTabList.create(cronTabSpec); } diff --git a/core/src/main/java/hudson/util/ArgumentListBuilder.java b/core/src/main/java/hudson/util/ArgumentListBuilder.java index 39e19adcbe..9a5a037fe0 100644 --- a/core/src/main/java/hudson/util/ArgumentListBuilder.java +++ b/core/src/main/java/hudson/util/ArgumentListBuilder.java @@ -39,7 +39,7 @@ import java.io.Serializable; import java.io.File; import java.io.IOException; import java.util.Set; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Used to build up arguments for a process invocation. @@ -138,7 +138,7 @@ public class ArgumentListBuilder implements Serializable, Cloneable { /** * @since 2.72 */ - public ArgumentListBuilder add(@Nonnull Iterable args) { + public ArgumentListBuilder add(@NonNull Iterable args) { for (String arg : args) { add(arg); } diff --git a/core/src/main/java/hudson/util/AtomicFileWriter.java b/core/src/main/java/hudson/util/AtomicFileWriter.java index 27234651b2..6d2f4fb329 100644 --- a/core/src/main/java/hudson/util/AtomicFileWriter.java +++ b/core/src/main/java/hudson/util/AtomicFileWriter.java @@ -25,8 +25,8 @@ package hudson.util; import jenkins.util.SystemProperties; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -80,7 +80,7 @@ public class AtomicFileWriter extends Writer { * @deprecated Use {@link #AtomicFileWriter(Path, Charset)} */ @Deprecated - public AtomicFileWriter(@Nonnull File f, @Nullable String encoding) throws IOException { + public AtomicFileWriter(@NonNull File f, @Nullable String encoding) throws IOException { this(toPath(f), encoding == null ? Charset.defaultCharset() : Charset.forName(encoding)); } @@ -92,7 +92,7 @@ public class AtomicFileWriter extends Writer { * @return the path for that file * @see File#toPath() */ - private static Path toPath(@Nonnull File file) throws IOException { + private static Path toPath(@NonNull File file) throws IOException { try { return file.toPath(); } catch (InvalidPathException e) { @@ -104,7 +104,7 @@ public class AtomicFileWriter extends Writer { * @param destinationPath the destination path where to write the content when committed. * @param charset File charset to write. */ - public AtomicFileWriter(@Nonnull Path destinationPath, @Nonnull Charset charset) throws IOException { + public AtomicFileWriter(@NonNull Path destinationPath, @NonNull Charset charset) throws IOException { // See FileChannelWriter docs to understand why we do not cause a force() call on flush() from AtomicFileWriter. this(destinationPath, charset, false, true); } @@ -119,7 +119,7 @@ public class AtomicFileWriter extends Writer { * @deprecated use {@link AtomicFileWriter#AtomicFileWriter(Path, Charset)} */ @Deprecated - public AtomicFileWriter(@Nonnull Path destinationPath, @Nonnull Charset charset, boolean integrityOnFlush, boolean integrityOnClose) throws IOException { + public AtomicFileWriter(@NonNull Path destinationPath, @NonNull Charset charset, boolean integrityOnFlush, boolean integrityOnClose) throws IOException { if (charset == null) { // be extra-defensive if people don't care throw new IllegalArgumentException("charset is null"); } diff --git a/core/src/main/java/hudson/util/BootFailure.java b/core/src/main/java/hudson/util/BootFailure.java index 5649d79d6a..ae122e8c42 100644 --- a/core/src/main/java/hudson/util/BootFailure.java +++ b/core/src/main/java/hudson/util/BootFailure.java @@ -4,7 +4,7 @@ import hudson.WebAppMain; import jenkins.util.groovy.GroovyHookScript; import org.kohsuke.stapler.WebApp; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import javax.servlet.ServletContext; import java.io.BufferedReader; import java.io.File; diff --git a/core/src/main/java/hudson/util/DOSToUnixPathHelper.java b/core/src/main/java/hudson/util/DOSToUnixPathHelper.java index c1eb489efc..9dcb9aa734 100644 --- a/core/src/main/java/hudson/util/DOSToUnixPathHelper.java +++ b/core/src/main/java/hudson/util/DOSToUnixPathHelper.java @@ -65,7 +65,7 @@ class DOSToUnixPathHelper { tokenizedPathBuilder.append(_dir.replace('\\', '/')); - if (checkPrefix(_dir + File.pathSeparator + exe, helper)) + if (checkPrefix(_dir + File.separator + exe, helper)) return; } tokenizedPathBuilder.append('.'); diff --git a/core/src/main/java/hudson/util/DescriptorList.java b/core/src/main/java/hudson/util/DescriptorList.java index 35c3e1dcbb..8473297898 100644 --- a/core/src/main/java/hudson/util/DescriptorList.java +++ b/core/src/main/java/hudson/util/DescriptorList.java @@ -29,6 +29,7 @@ import hudson.model.Describable; import hudson.model.Descriptor; import hudson.model.Descriptor.FormException; import jenkins.model.Jenkins; +import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.kohsuke.stapler.Stapler; @@ -36,7 +37,7 @@ import java.util.AbstractList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * List of {@link Descriptor}s. @@ -150,7 +151,8 @@ public final class DescriptorList> extends AbstractList * from the structured form submission data posted * by a radio button group. * @param config Submitted configuration for Radio List - * @return new instance or {@code null} if none was selected in the radio list + * @return New instance. + * {@code null} if none was selected in the radio list or if the value is filtered by a {@link hudson.model.DescriptorVisibilityFilter} * @throws FormException Data submission error */ @CheckForNull @@ -167,12 +169,17 @@ public final class DescriptorList> extends AbstractList * by a radio button group. * @param parent JSON, which contains the configuration entry for the radio list * @param name Name of the configuration entry for the radio list - * @return new instance or {@code null} if none was selected in the radio list + * @return New instance. + * {@code null} if none was selected in the radio list or if the value is filtered by a {@link hudson.model.DescriptorVisibilityFilter} * @throws FormException Data submission error */ @CheckForNull public T newInstanceFromRadioList(JSONObject parent, String name) throws FormException { - return newInstanceFromRadioList(parent.getJSONObject(name)); + try { + return newInstanceFromRadioList(parent.getJSONObject(name)); + } catch (JSONException ex) { + throw new FormException(ex, name); + } } /** diff --git a/core/src/main/java/hudson/util/FormFillFailure.java b/core/src/main/java/hudson/util/FormFillFailure.java index 16b8e6435a..dd1d62e6f7 100644 --- a/core/src/main/java/hudson/util/FormFillFailure.java +++ b/core/src/main/java/hudson/util/FormFillFailure.java @@ -27,7 +27,7 @@ import hudson.Functions; import hudson.Util; import java.io.IOException; import java.util.Locale; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; import jenkins.model.Jenkins; @@ -52,11 +52,11 @@ public abstract class FormFillFailure extends IOException implements HttpRespons * * @param message Human readable message to be sent. */ - public static FormFillFailure error(@Nonnull String message) { + public static FormFillFailure error(@NonNull String message) { return errorWithMarkup(Util.escape(message)); } - public static FormFillFailure warning(@Nonnull String message) { + public static FormFillFailure warning(@NonNull String message) { return warningWithMarkup(Util.escape(message)); } @@ -128,7 +128,7 @@ public abstract class FormFillFailure extends IOException implements HttpRespons return _errorWithMarkup(message, FormValidation.Kind.WARNING); } - private static FormFillFailure _errorWithMarkup(@Nonnull final String message, final FormValidation.Kind kind) { + private static FormFillFailure _errorWithMarkup(@NonNull final String message, final FormValidation.Kind kind) { return new FormFillFailure(kind, message) { public String renderHtml() { StaplerRequest req = Stapler.getCurrentRequest(); diff --git a/core/src/main/java/hudson/util/FormValidation.java b/core/src/main/java/hudson/util/FormValidation.java index 94a0c4b2ac..d3a88a5b94 100644 --- a/core/src/main/java/hudson/util/FormValidation.java +++ b/core/src/main/java/hudson/util/FormValidation.java @@ -43,7 +43,7 @@ import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.Stapler; import org.springframework.util.StringUtils; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import java.io.File; @@ -219,7 +219,7 @@ public abstract class FormValidation extends IOException implements HttpResponse * @return Validation of the least successful kind aggregating all child messages. * @since 1.590 */ - public static @Nonnull FormValidation aggregate(@Nonnull Collection validations) { + public static @NonNull FormValidation aggregate(@NonNull Collection validations) { if (validations == null || validations.isEmpty()) return FormValidation.ok(); if (validations.size() == 1) return validations.iterator().next(); diff --git a/core/src/main/java/hudson/util/Graph.java b/core/src/main/java/hudson/util/Graph.java index bef25b2d9e..de98dd99a8 100644 --- a/core/src/main/java/hudson/util/Graph.java +++ b/core/src/main/java/hudson/util/Graph.java @@ -36,8 +36,8 @@ import java.io.IOException; import java.util.Calendar; import java.awt.image.BufferedImage; import java.awt.*; -import javax.annotation.Nonnull; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * A JFreeChart-generated graph that's bound to UI. @@ -98,7 +98,7 @@ public abstract class Graph { return graph.createBufferedImage(Integer.parseInt(w),Integer.parseInt(h),info); } - @Nonnull private static Color stringToColor(@CheckForNull String s) { + @NonNull private static Color stringToColor(@CheckForNull String s) { if (s != null) { try { return Color.decode("0x" + s); diff --git a/core/src/main/java/hudson/util/HistoricalSecrets.java b/core/src/main/java/hudson/util/HistoricalSecrets.java index 7b1d0ee3bb..ed0bbf9d4d 100644 --- a/core/src/main/java/hudson/util/HistoricalSecrets.java +++ b/core/src/main/java/hudson/util/HistoricalSecrets.java @@ -81,9 +81,15 @@ public class HistoricalSecrets { */ @Deprecated /*package*/ static SecretKey getLegacyKey() throws GeneralSecurityException { - String secret = Secret.SECRET; - if(secret==null) return Jenkins.get().getSecretKeyAsAES128(); - return Util.toAes128Key(secret); + if (Secret.SECRET != null) { + return Util.toAes128Key(Secret.SECRET); + } + Jenkins j = Jenkins.getInstanceOrNull(); + if (j != null) { + return j.getSecretKeyAsAES128(); + } else { + return Util.toAes128Key("mock"); + } } static final String MAGIC = "::::MAGIC::::"; diff --git a/core/src/main/java/hudson/util/HttpResponses.java b/core/src/main/java/hudson/util/HttpResponses.java index 78de2ecfb3..417b8fa1de 100644 --- a/core/src/main/java/hudson/util/HttpResponses.java +++ b/core/src/main/java/hudson/util/HttpResponses.java @@ -29,7 +29,7 @@ import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import java.io.File; import java.io.IOException; @@ -64,7 +64,7 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * * @since 2.0 */ - public static HttpResponse okJSON(@Nonnull JSONObject data) { + public static HttpResponse okJSON(@NonNull JSONObject data) { return new JSONObjectResponse(data); } @@ -74,7 +74,7 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * * @since 2.0 */ - public static HttpResponse okJSON(@Nonnull JSONArray data) { + public static HttpResponse okJSON(@NonNull JSONArray data) { return new JSONObjectResponse(data); } @@ -84,7 +84,7 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * * @since 2.0 */ - public static HttpResponse okJSON(@Nonnull Map data) { + public static HttpResponse okJSON(@NonNull Map data) { return new JSONObjectResponse(data); } @@ -95,7 +95,7 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * * @since 2.0 */ - public static HttpResponse errorJSON(@Nonnull String message) { + public static HttpResponse errorJSON(@NonNull String message) { return new JSONObjectResponse().error(message); } @@ -107,7 +107,7 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * * @since 2.119 */ - public static HttpResponse errorJSON(@Nonnull String message, @Nonnull Map data) { + public static HttpResponse errorJSON(@NonNull String message, @NonNull Map data) { return new JSONObjectResponse(data).error(message); } @@ -119,7 +119,7 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * * @since 2.115 */ - public static HttpResponse errorJSON(@Nonnull String message, @Nonnull JSONObject data) { + public static HttpResponse errorJSON(@NonNull String message, @NonNull JSONObject data) { return new JSONObjectResponse(data).error(message); } @@ -131,7 +131,7 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * * @since 2.115 */ - public static HttpResponse errorJSON(@Nonnull String message, @Nonnull JSONArray data) { + public static HttpResponse errorJSON(@NonNull String message, @NonNull JSONArray data) { return new JSONObjectResponse(data).error(message); } @@ -156,7 +156,7 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * Create a response containing the supplied "data". * @param data The data. */ - JSONObjectResponse(@Nonnull JSONObject data) { + JSONObjectResponse(@NonNull JSONObject data) { this(); this.jsonObject.put("data", data); } @@ -165,7 +165,7 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * Create a response containing the supplied "data". * @param data The data. */ - JSONObjectResponse(@Nonnull JSONArray data) { + JSONObjectResponse(@NonNull JSONArray data) { this(); this.jsonObject.put("data", data); } @@ -174,7 +174,7 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * Create a response containing the supplied "data". * @param data The data. */ - JSONObjectResponse(@Nonnull Map data) { + JSONObjectResponse(@NonNull Map data) { this(); this.jsonObject.put("data", JSONObject.fromObject(data)); } @@ -184,7 +184,7 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * @param message The error "message" set on the response. * @return {@link this} object. */ - @Nonnull JSONObjectResponse error(@Nonnull String message) { + @NonNull JSONObjectResponse error(@NonNull String message) { status("error"); this.jsonObject.put("message", message); return this; @@ -194,11 +194,11 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { * Get the JSON response object. * @return The JSON response object. */ - @Nonnull JSONObject getJsonObject() { + @NonNull JSONObject getJsonObject() { return jsonObject; } - private @Nonnull JSONObjectResponse status(@Nonnull String status) { + private @NonNull JSONObjectResponse status(@NonNull String status) { this.jsonObject.put("status", status); return this; } diff --git a/core/src/main/java/hudson/util/Iterators.java b/core/src/main/java/hudson/util/Iterators.java index 25b45f0c85..f2a1572330 100644 --- a/core/src/main/java/hudson/util/Iterators.java +++ b/core/src/main/java/hudson/util/Iterators.java @@ -35,7 +35,7 @@ import java.util.ListIterator; import java.util.AbstractList; import java.util.Set; import java.util.HashSet; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -413,7 +413,7 @@ public class Iterators { * @param count a nonnegative count */ @Restricted(NoExternalUse.class) - public static void skip(@Nonnull Iterator iterator, int count) { + public static void skip(@NonNull Iterator iterator, int count) { if (count < 0) { throw new IllegalArgumentException(); } diff --git a/core/src/main/java/hudson/util/KeyedDataStorage.java b/core/src/main/java/hudson/util/KeyedDataStorage.java index 93f15b6a09..262514dadf 100644 --- a/core/src/main/java/hudson/util/KeyedDataStorage.java +++ b/core/src/main/java/hudson/util/KeyedDataStorage.java @@ -31,8 +31,8 @@ import java.lang.ref.SoftReference; import java.text.MessageFormat; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Convenient base class for implementing data storage. @@ -107,7 +107,7 @@ public abstract class KeyedDataStorage { * Additional parameters needed to create a new data object. Can be null. * @throws IOException Loading error */ - public @Nonnull T getOrCreate(String key, P createParams) throws IOException { + public @NonNull T getOrCreate(String key, P createParams) throws IOException { return get(key,true,createParams); } @@ -126,7 +126,7 @@ public abstract class KeyedDataStorage { * @return Item with the specified {@code key} * @throws IOException Loading error */ - protected @CheckForNull T get(@Nonnull String key, boolean createIfNotExist, P createParams) throws IOException { + protected @CheckForNull T get(@NonNull String key, boolean createIfNotExist, P createParams) throws IOException { while(true) { totalQuery.incrementAndGet(); Object value = core.get(key); @@ -219,7 +219,7 @@ public abstract class KeyedDataStorage { * {@link IOException} (or any other exception) and that will be * propagated to the caller. */ - protected abstract @Nonnull T create(@Nonnull String key, @Nonnull P createParams) throws IOException; + protected abstract @NonNull T create(@NonNull String key, @NonNull P createParams) throws IOException; public void resetPerformanceStats() { totalQuery.set(0); diff --git a/core/src/main/java/hudson/util/LogTaskListener.java b/core/src/main/java/hudson/util/LogTaskListener.java index bb3afc41e1..33b2ca2318 100644 --- a/core/src/main/java/hudson/util/LogTaskListener.java +++ b/core/src/main/java/hudson/util/LogTaskListener.java @@ -24,7 +24,7 @@ package hudson.util; -import hudson.console.ConsoleNote; +import hudson.console.PlainTextConsoleOutputStream; import hudson.model.TaskListener; import java.io.ByteArrayOutputStream; import java.io.Closeable; @@ -47,7 +47,7 @@ public class LogTaskListener extends AbstractTaskListener implements TaskListene private final TaskListener delegate; public LogTaskListener(Logger logger, Level level) { - delegate = new StreamTaskListener(new LogOutputStream(logger, level, new Throwable().getStackTrace()[1])); + delegate = new StreamTaskListener(new PlainTextConsoleOutputStream(new LogOutputStream(logger, level, new Throwable().getStackTrace()[1]))); } @Override @@ -55,17 +55,12 @@ public class LogTaskListener extends AbstractTaskListener implements TaskListene return delegate.getLogger(); } - @Override - @SuppressWarnings("rawtypes") - public void annotate(ConsoleNote ann) { - // no annotation support - } - @Override public void close() { delegate.getLogger().close(); } + // TODO a bit simpler to use LineTransformationOutputStream private static class LogOutputStream extends OutputStream { private final Logger logger; diff --git a/core/src/main/java/hudson/util/Memoizer.java b/core/src/main/java/hudson/util/Memoizer.java deleted file mode 100644 index cce36b315c..0000000000 --- a/core/src/main/java/hudson/util/Memoizer.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2009, Sun Microsystems, 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 hudson.util; - -import java.util.concurrent.ConcurrentHashMap; - -/** - * Implements memoization semantics. - * - *

- * Conceptually a function from K → V that computes values lazily and remembers the results. - * Often used to implement a data store per key. - * - * @author Kohsuke Kawaguchi - * @since 1.281 - * @deprecated Simply use {@link ConcurrentHashMap#computeIfAbsent}. - */ -@Deprecated -public abstract class Memoizer { - private final ConcurrentHashMap store = new ConcurrentHashMap<>(); - - public V get(K key) { - V v = store.get(key); - if(v!=null) return v; - - // TODO: if we want to, we can avoid locking altogether by putting a sentinel value - // that represents "the value is being computed". FingerprintMap does this. - synchronized (this) { - v = store.get(key); - if(v!=null) return v; - - v = compute(key); - store.put(key,v); - return v; - } - } - - /** - * Creates a new instance. - */ - public abstract V compute(K key); - - /** - * Clears all the computed values. - */ - public void clear() { - store.clear(); - } - - /** - * Provides a snapshot view of all {@code V}s. - */ - public Iterable values() { - return store.values(); - } -} diff --git a/core/src/main/java/hudson/util/MultipartFormDataParser.java b/core/src/main/java/hudson/util/MultipartFormDataParser.java index 39cac1c987..14b1517725 100644 --- a/core/src/main/java/hudson/util/MultipartFormDataParser.java +++ b/core/src/main/java/hudson/util/MultipartFormDataParser.java @@ -29,7 +29,7 @@ import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.lang.ArrayUtils; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import javax.servlet.http.HttpServletRequest; import javax.servlet.ServletException; import java.util.Map; diff --git a/core/src/main/java/hudson/util/PluginServletFilter.java b/core/src/main/java/hudson/util/PluginServletFilter.java index 78e9eb73a9..2929024782 100644 --- a/core/src/main/java/hudson/util/PluginServletFilter.java +++ b/core/src/main/java/hudson/util/PluginServletFilter.java @@ -28,7 +28,7 @@ import hudson.security.SecurityRealm; import java.util.ArrayList; import jenkins.model.Jenkins; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; diff --git a/core/src/main/java/hudson/util/ProcessKillingVeto.java b/core/src/main/java/hudson/util/ProcessKillingVeto.java index b8b874e42a..1814afa32b 100644 --- a/core/src/main/java/hudson/util/ProcessKillingVeto.java +++ b/core/src/main/java/hudson/util/ProcessKillingVeto.java @@ -30,8 +30,8 @@ import hudson.util.ProcessTreeRemoting.IOSProcess; import java.util.Collections; import java.util.List; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.util.JenkinsJVM; @@ -58,14 +58,14 @@ public abstract class ProcessKillingVeto implements ExtensionPoint { /** * @param message A string describing the reason for the veto */ - public VetoCause(@Nonnull String message) { + public VetoCause(@NonNull String message) { this.message = message; } /** * @return A string describing the reason for the veto. */ - public @Nonnull String getMessage() { + public @NonNull String getMessage() { return message; } } @@ -99,5 +99,5 @@ public abstract class ProcessKillingVeto implements ExtensionPoint { * null else. */ @CheckForNull - public abstract VetoCause vetoProcessKilling(@Nonnull IOSProcess p); + public abstract VetoCause vetoProcessKilling(@NonNull IOSProcess p); } diff --git a/core/src/main/java/hudson/util/ProcessTree.java b/core/src/main/java/hudson/util/ProcessTree.java index 4a0db31765..8e7bcb7e6c 100644 --- a/core/src/main/java/hudson/util/ProcessTree.java +++ b/core/src/main/java/hudson/util/ProcessTree.java @@ -33,7 +33,7 @@ import hudson.FilePath; import hudson.Util; import hudson.remoting.Channel; import hudson.remoting.VirtualChannel; -import hudson.slaves.SlaveComputer; +import jenkins.agents.AgentComputerUtil; import hudson.util.ProcessKillingVeto.VetoCause; import hudson.util.ProcessTree.OSProcess; import hudson.util.ProcessTreeRemoting.IOSProcess; @@ -73,13 +73,13 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import static com.sun.jna.Pointer.NULL; import jenkins.util.SystemProperties; import static hudson.util.jna.GNUCLibrary.LIBC; import static java.util.logging.Level.FINER; import static java.util.logging.Level.FINEST; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Represents a snapshot of the process tree of the current system. @@ -132,7 +132,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, /** * Lists all the processes in the system. */ - @Nonnull + @NonNull public final Iterator iterator() { return processes.values().iterator(); } @@ -143,7 +143,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * this process has already finished.) */ @CheckForNull - public abstract OSProcess get(@Nonnull Process proc); + public abstract OSProcess get(@NonNull Process proc); /** * Kills all the processes that have matching environment variables. @@ -157,7 +157,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * them all. This is suitable for locating daemon processes * that cannot be tracked by the regular ancestor/descendant relationship. */ - public abstract void killAll(@Nonnull Map modelEnvVars) throws InterruptedException; + public abstract void killAll(@NonNull Map modelEnvVars) throws InterruptedException; /** * The time to wait between sending Ctrl+C and killing the process. (JENKINS-17116) @@ -189,11 +189,11 @@ public abstract class ProcessTree implements Iterable, IProcessTree, /** * Obtains the list of killers. */ - @Nonnull + @NonNull /*package*/ final List getKillers() throws InterruptedException { if (killers==null) try { - VirtualChannel channelToMaster = SlaveComputer.getChannelToMaster(); + VirtualChannel channelToMaster = AgentComputerUtil.getChannelToMaster(); if (channelToMaster!=null) { killers = channelToMaster.call(new ListAll()); } else { @@ -243,7 +243,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, /** * Immediate child processes. */ - @Nonnull + @NonNull public final List getChildren() { List r = new ArrayList<>(); for (OSProcess p : ProcessTree.this) @@ -288,7 +288,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, // Quick check, does anything exist to check against if (!skipVetoes) { try { - VirtualChannel channelToMaster = SlaveComputer.getChannelToMaster(); + VirtualChannel channelToMaster = AgentComputerUtil.getChannelToMaster(); if (channelToMaster!=null) { CheckVetoes vetoCheck = new CheckVetoes(this); causeMessage = channelToMaster.call(vetoCheck); @@ -313,7 +313,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * On Windows, where the OS models command-line arguments as a single string, this method * computes the approximated tokenization. */ - @Nonnull + @NonNull public abstract List getArguments(); /** @@ -323,7 +323,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * empty map if failed (for example because the process is already dead, * or the permission was denied.) */ - @Nonnull + @NonNull public abstract EnvVars getEnvironmentVariables(); /** @@ -429,7 +429,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, // Check for the existance of vetoers if I don't know already if (vetoersExist == null) { try { - VirtualChannel channelToMaster = SlaveComputer.getChannelToMaster(); + VirtualChannel channelToMaster = AgentComputerUtil.getChannelToMaster(); if (channelToMaster != null) { vetoersExist = channelToMaster.call(new DoVetoersExist()); } @@ -480,7 +480,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * Empty process list as a default value if the platform doesn't support it. */ /*package*/ static final ProcessTree DEFAULT = new Local() { - public OSProcess get(@Nonnull final Process proc) { + public OSProcess get(@NonNull final Process proc) { return new OSProcess(-1) { @CheckForNull public OSProcess getParent() { @@ -499,19 +499,19 @@ public abstract class ProcessTree implements Iterable, IProcessTree, killByKiller(); } - @Nonnull + @NonNull public List getArguments() { return Collections.emptyList(); } - @Nonnull + @NonNull public EnvVars getEnvironmentVariables() { return new EnvVars(); } }; } - public void killAll(@Nonnull Map modelEnvVars) { + public void killAll(@NonNull Map modelEnvVars) { // no-op } }; @@ -586,7 +586,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, } while (System.nanoTime() < deadline); } - @Nonnull + @NonNull @Override public synchronized List getArguments() { if(args==null) { @@ -595,7 +595,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, return args; } - @Nonnull + @NonNull @Override public synchronized EnvVars getEnvironmentVariables() { try { @@ -665,12 +665,12 @@ public abstract class ProcessTree implements Iterable, IProcessTree, @CheckForNull @Override - public OSProcess get(@Nonnull Process proc) { + public OSProcess get(@NonNull Process proc) { return get(new WinProcess(proc).getPid()); } @Override - public void killAll(@Nonnull Map modelEnvVars) throws InterruptedException { + public void killAll(@NonNull Map modelEnvVars) throws InterruptedException { for( OSProcess p : this) { if(p.getPid()<10) continue; // ignore system processes like "idle process" @@ -701,7 +701,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, WinProcess.enableDebugPrivilege(); } - private static boolean hasMatchingEnvVars(@Nonnull OSProcess p, @Nonnull Map modelEnvVars) + private static boolean hasMatchingEnvVars(@NonNull OSProcess p, @NonNull Map modelEnvVars) throws WindowsOSProcessException { if (p instanceof WindowsOSProcess) { return ((WindowsOSProcess)p).hasMatchingEnvVars2(modelEnvVars); @@ -724,11 +724,11 @@ public abstract class ProcessTree implements Iterable, IProcessTree, @CheckForNull @Override - public OSProcess get(@Nonnull Process proc) { + public OSProcess get(@NonNull Process proc) { return get(UnixReflection.pid(proc)); } - public void killAll(@Nonnull Map modelEnvVars) throws InterruptedException { + public void killAll(@NonNull Map modelEnvVars) throws InterruptedException { for (OSProcess p : this) if(p.hasMatchingEnvVars(modelEnvVars)) p.killRecursively(); @@ -850,7 +850,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, * empty list if failed (for example because the process is already dead, * or the permission was denied.) */ - @Nonnull + @NonNull public abstract List getArguments(); } @@ -919,7 +919,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, } //TODO: We ideally need to update ProcessTree APIs to Support Long (JENKINS-53799). - public static int pid(@Nonnull Process proc) { + public static int pid(@NonNull Process proc) { try { if (JAVA8_PID_FIELD != null) { return JAVA8_PID_FIELD.getInt(proc); @@ -975,7 +975,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, return get(ppid); } - @Nonnull + @NonNull public synchronized List getArguments() { if(arguments!=null) return arguments; @@ -998,7 +998,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, return arguments; } - @Nonnull + @NonNull public synchronized EnvVars getEnvironmentVariables() { if(envVars !=null) return envVars; @@ -1197,7 +1197,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, return get(ppid); } - @Nonnull + @NonNull public synchronized List getArguments() { if (arguments != null) return arguments; @@ -1244,7 +1244,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, return arguments; } - @Nonnull + @NonNull public synchronized EnvVars getEnvironmentVariables() { if(envVars != null) return envVars; @@ -1484,7 +1484,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, return get(ppid); } - @Nonnull + @NonNull public synchronized List getArguments() { if(arguments!=null) return arguments; @@ -1520,7 +1520,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, return arguments; } - @Nonnull + @NonNull public synchronized EnvVars getEnvironmentVariables() { if(envVars !=null) return envVars; @@ -1681,7 +1681,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, return get(ppid); } - @Nonnull + @NonNull public synchronized EnvVars getEnvironmentVariables() { if(envVars !=null) return envVars; @@ -1689,7 +1689,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, return envVars; } - @Nonnull + @NonNull public List getArguments() { if(arguments !=null) return arguments; @@ -1872,12 +1872,12 @@ public abstract class ProcessTree implements Iterable, IProcessTree, @CheckForNull @Override - public OSProcess get(@Nonnull Process proc) { + public OSProcess get(@NonNull Process proc) { return null; } @Override - public void killAll(@Nonnull Map modelEnvVars) throws InterruptedException { + public void killAll(@NonNull Map modelEnvVars) throws InterruptedException { proxy.killAll(modelEnvVars); } @@ -1910,12 +1910,12 @@ public abstract class ProcessTree implements Iterable, IProcessTree, proxy.killRecursively(); } - @Nonnull + @NonNull public List getArguments() { return proxy.getArguments(); } - @Nonnull + @NonNull public EnvVars getEnvironmentVariables() { return proxy.getEnvironmentVariables(); } diff --git a/core/src/main/java/hudson/util/ProcessTreeRemoting.java b/core/src/main/java/hudson/util/ProcessTreeRemoting.java index d031c9dffd..b495e81ca1 100644 --- a/core/src/main/java/hudson/util/ProcessTreeRemoting.java +++ b/core/src/main/java/hudson/util/ProcessTreeRemoting.java @@ -3,8 +3,8 @@ package hudson.util; import hudson.EnvVars; import hudson.util.ProcessTree.ProcessCallable; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.lang.reflect.Proxy; import java.util.List; @@ -19,7 +19,7 @@ import java.util.Map; */ public class ProcessTreeRemoting { public interface IProcessTree { - void killAll(@Nonnull Map modelEnvVars) throws InterruptedException; + void killAll(@NonNull Map modelEnvVars) throws InterruptedException; } public interface IOSProcess { @@ -28,9 +28,9 @@ public class ProcessTreeRemoting { IOSProcess getParent(); void kill() throws InterruptedException; void killRecursively() throws InterruptedException; - @Nonnull + @NonNull List getArguments(); - @Nonnull + @NonNull EnvVars getEnvironmentVariables(); T act(ProcessCallable callable) throws IOException, InterruptedException; } diff --git a/core/src/main/java/hudson/util/ReflectionUtils.java b/core/src/main/java/hudson/util/ReflectionUtils.java index faa6afb99a..7ff60e84b0 100644 --- a/core/src/main/java/hudson/util/ReflectionUtils.java +++ b/core/src/main/java/hudson/util/ReflectionUtils.java @@ -37,7 +37,7 @@ import java.util.AbstractList; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Utility code for reflection. diff --git a/core/src/main/java/hudson/util/RemotingDiagnostics.java b/core/src/main/java/hudson/util/RemotingDiagnostics.java index ec341b3bef..57e6ec210f 100644 --- a/core/src/main/java/hudson/util/RemotingDiagnostics.java +++ b/core/src/main/java/hudson/util/RemotingDiagnostics.java @@ -41,7 +41,7 @@ import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.WebMethod; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; @@ -107,7 +107,7 @@ public final class RemotingDiagnostics { /** * Executes Groovy script remotely. */ - public static String executeGroovy(String script, @Nonnull VirtualChannel channel) throws IOException, InterruptedException { + public static String executeGroovy(String script, @NonNull VirtualChannel channel) throws IOException, InterruptedException { return channel.call(new Script(script)); } @@ -196,7 +196,7 @@ public final class RemotingDiagnostics { @WebMethod(name="heapdump.hprof") public void doHeapDump(StaplerRequest req, StaplerResponse rsp) throws IOException, InterruptedException { - owner.checkPermission(Jenkins.RUN_SCRIPTS); + owner.checkPermission(Jenkins.ADMINISTER); rsp.setContentType("application/octet-stream"); FilePath dump = obtain(); diff --git a/core/src/main/java/hudson/util/Retrier.java b/core/src/main/java/hudson/util/Retrier.java index 1e3df9e8b5..b4dcb3ef13 100644 --- a/core/src/main/java/hudson/util/Retrier.java +++ b/core/src/main/java/hudson/util/Retrier.java @@ -3,8 +3,9 @@ package hudson.util; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Arrays; import java.util.concurrent.Callable; import java.util.function.BiFunction; import java.util.function.BiPredicate; @@ -121,7 +122,7 @@ public class Retrier { * @param attempts number of attempts * @return this builder */ - public @Nonnull Builder withAttempts(int attempts) { + public @NonNull Builder withAttempts(int attempts) { this.attempts = attempts; return this; } @@ -131,7 +132,7 @@ public class Retrier { * @param millis milliseconds to wait * @return this builder */ - public @Nonnull Builder withDelay(long millis) { + public @NonNull Builder withDelay(long millis) { this.delay = millis; return this; } @@ -143,8 +144,8 @@ public class Retrier { * @param exceptions exceptions that indicate that the action was failed. * @return this builder */ - public @Nonnull Builder withDuringActionExceptions(@CheckForNull Class[] exceptions) { - this.duringActionExceptions = exceptions; + public @NonNull Builder withDuringActionExceptions(@CheckForNull Class[] exceptions) { + this.duringActionExceptions = exceptions != null ? Arrays.copyOf(exceptions, exceptions.length) : null; return this; } @@ -154,7 +155,7 @@ public class Retrier { * @param exceptionListener the listener to call to * @return this builder */ - public @Nonnull Builder withDuringActionExceptionListener(@Nonnull BiFunction exceptionListener) { + public @NonNull Builder withDuringActionExceptionListener(@NonNull BiFunction exceptionListener) { this.duringActionExceptionListener = exceptionListener; return this; } @@ -166,7 +167,7 @@ public class Retrier { * @param action name of the action to perform, for messages purposes. */ - public Builder(@Nonnull Callable callable, @Nonnull BiPredicate checkResult, @Nonnull String action) { + public Builder(@NonNull Callable callable, @NonNull BiPredicate checkResult, @NonNull String action) { this.callable = callable; this.action = action; this.checkResult = checkResult; @@ -176,7 +177,7 @@ public class Retrier { * Create a Retrier object with the specification set in this builder. * @return the retrier */ - public @Nonnull Retrier build() { + public @NonNull Retrier build() { return new Retrier<>(this); } } diff --git a/core/src/main/java/hudson/util/RobustReflectionConverter.java b/core/src/main/java/hudson/util/RobustReflectionConverter.java index a2907d2517..976d1755f7 100644 --- a/core/src/main/java/hudson/util/RobustReflectionConverter.java +++ b/core/src/main/java/hudson/util/RobustReflectionConverter.java @@ -54,8 +54,8 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import static java.util.logging.Level.FINE; import java.util.logging.Logger; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.GuardedBy; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.GuardedBy; import jenkins.util.xstream.CriticalXStreamException; /** @@ -75,7 +75,7 @@ public class RobustReflectionConverter implements Converter { protected final Mapper mapper; protected transient SerializationMethodInvoker serializationMethodInvoker; private transient ReflectionProvider pureJavaReflectionProvider; - private final @Nonnull XStream2.ClassOwnership classOwnership; + private final @NonNull XStream2.ClassOwnership classOwnership; /** There are typically few critical fields around, but we end up looking up in this map a lot. in addition, this map is really only written to during static initialization, so we should use reader writer lock to avoid locking as much as possible. In addition, to avoid looking up diff --git a/core/src/main/java/hudson/util/Secret.java b/core/src/main/java/hudson/util/Secret.java index b77c7d8a10..bbda0d6d6a 100644 --- a/core/src/main/java/hudson/util/Secret.java +++ b/core/src/main/java/hudson/util/Secret.java @@ -31,7 +31,6 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import jenkins.util.SystemProperties; import java.util.Arrays; -import jenkins.model.Jenkins; import hudson.Util; import jenkins.security.CryptoConfidentialKey; import org.kohsuke.stapler.Stapler; @@ -44,8 +43,8 @@ import java.security.GeneralSecurityException; import java.util.Base64; import java.util.logging.Logger; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -71,7 +70,7 @@ public final class Secret implements Serializable { /** * Unencrypted secret text. */ - @Nonnull + @NonNull private final String value; private byte[] iv; @@ -105,7 +104,7 @@ public final class Secret implements Serializable { * Before using this method, ask yourself if you'd be better off using {@link Secret#toString(Secret)} * to avoid NPE. */ - @Nonnull + @NonNull public String getPlainText() { return value; } @@ -246,7 +245,7 @@ public final class Secret implements Serializable { *

* Useful for recovering a value from a form field. */ - @Nonnull + @NonNull public static Secret fromString(@CheckForNull String data) { data = Util.fixNull(data); Secret s = decrypt(data); @@ -259,7 +258,7 @@ public final class Secret implements Serializable { * To be consistent with {@link #fromString(String)}, this method doesn't distinguish * empty password and null password. */ - @Nonnull + @NonNull public static String toString(@CheckForNull Secret s) { return s==null ? "" : s.value; } @@ -289,8 +288,10 @@ public final class Secret implements Serializable { private static final String PROVIDER = SystemProperties.getString(Secret.class.getName()+".provider"); /** - * For testing only. Override the secret key so that we can test this class without {@link Jenkins}. + * For testing only. + * @deprecated Normally unnecessary. */ + @Deprecated /*package*/ static String SECRET = null; /** @@ -303,6 +304,13 @@ public final class Secret implements Serializable { static { Stapler.CONVERT_UTILS.register(new org.apache.commons.beanutils.Converter() { public Secret convert(Class type, Object value) { + if (value == null) { + return null; + } + if (value instanceof Secret) { + return (Secret) value; + } + return Secret.fromString(value.toString()); } }, Secret.class); diff --git a/core/src/main/java/hudson/util/SequentialExecutionQueue.java b/core/src/main/java/hudson/util/SequentialExecutionQueue.java index d2ba8209c0..d6d035eef1 100644 --- a/core/src/main/java/hudson/util/SequentialExecutionQueue.java +++ b/core/src/main/java/hudson/util/SequentialExecutionQueue.java @@ -1,6 +1,6 @@ package hudson.util; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -59,7 +59,7 @@ public class SequentialExecutionQueue implements Executor { } - public synchronized void execute(@Nonnull Runnable item) { + public synchronized void execute(@NonNull Runnable item) { QueueEntry e = entries.get(item); if(e==null) { e = new QueueEntry(item); diff --git a/core/src/main/java/hudson/util/StreamTaskListener.java b/core/src/main/java/hudson/util/StreamTaskListener.java index 931a0b99bb..af1d84cca3 100644 --- a/core/src/main/java/hudson/util/StreamTaskListener.java +++ b/core/src/main/java/hudson/util/StreamTaskListener.java @@ -45,8 +45,8 @@ import java.util.logging.Logger; import jenkins.util.SystemProperties; import org.kohsuke.stapler.framework.io.WriterOutputStream; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; // TODO: AbstractTaskListener is empty now, but there are dependencies on that e.g. Ruby Runtime - JENKINS-48116) // The change needs API deprecation policy or external usages cleanup. @@ -60,7 +60,7 @@ import javax.annotation.Nonnull; * @author Kohsuke Kawaguchi */ public class StreamTaskListener extends AbstractTaskListener implements TaskListener, Closeable { - @Nonnull + @NonNull private PrintStream out; @CheckForNull private Charset charset; @@ -72,15 +72,15 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList * or use {@link #fromStdout()} or {@link #fromStderr()}. */ @Deprecated - public StreamTaskListener(@Nonnull PrintStream out) { + public StreamTaskListener(@NonNull PrintStream out) { this(out,null); } - public StreamTaskListener(@Nonnull OutputStream out) { + public StreamTaskListener(@NonNull OutputStream out) { this(out,null); } - public StreamTaskListener(@Nonnull OutputStream out, @CheckForNull Charset charset) { + public StreamTaskListener(@NonNull OutputStream out, @CheckForNull Charset charset) { try { if (charset == null) this.out = (out instanceof PrintStream) ? (PrintStream)out : new PrintStream(out, false); @@ -93,18 +93,18 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList } } - public StreamTaskListener(@Nonnull File out) throws IOException { + public StreamTaskListener(@NonNull File out) throws IOException { this(out,null); } - public StreamTaskListener(@Nonnull File out, @CheckForNull Charset charset) throws IOException { + public StreamTaskListener(@NonNull File out, @CheckForNull Charset charset) throws IOException { // don't do buffering so that what's written to the listener // gets reflected to the file immediately, which can then be // served to the browser immediately this(Files.newOutputStream(asPath(out)), charset); } - private static Path asPath(@Nonnull File out) throws IOException { + private static Path asPath(@NonNull File out) throws IOException { try { return out.toPath(); } catch (InvalidPathException e) { @@ -121,7 +121,7 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList * @throws IOException if the file could not be opened. * @since 1.651 */ - public StreamTaskListener(@Nonnull File out, boolean append, @CheckForNull Charset charset) throws IOException { + public StreamTaskListener(@NonNull File out, boolean append, @CheckForNull Charset charset) throws IOException { // don't do buffering so that what's written to the listener // gets reflected to the file immediately, which can then be // served to the browser immediately @@ -133,7 +133,7 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList ); } - public StreamTaskListener(@Nonnull Writer w) throws IOException { + public StreamTaskListener(@NonNull Writer w) throws IOException { this(new WriterOutputStream(w)); } diff --git a/core/src/main/java/hudson/util/StringConverter2.java b/core/src/main/java/hudson/util/StringConverter2.java deleted file mode 100644 index e993adedc5..0000000000 --- a/core/src/main/java/hudson/util/StringConverter2.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi - * - * 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 hudson.util; - -import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; -import com.thoughtworks.xstream.converters.basic.StringConverter; - -/** - * The default {@link StringConverter} in XStream - * uses {@link String#intern()}, which stresses the - * (rather limited) PermGen space with a large XML file. - * - *

- * Use this to avoid that (instead those strings will - * now be allocated to the heap space.) - * - * @author Kohsuke Kawaguchi - * @deprecated since 2009-11-06 - * Use {@link HeapSpaceStringConverter} instead. - */ -@Deprecated -public class StringConverter2 extends AbstractSingleValueConverter { - - public boolean canConvert(Class type) { - return type.equals(String.class); - } - - public Object fromString(String str) { - return str; - } -} diff --git a/core/src/main/java/hudson/util/TextFile.java b/core/src/main/java/hudson/util/TextFile.java index ee06f713a5..0830898c21 100644 --- a/core/src/main/java/hudson/util/TextFile.java +++ b/core/src/main/java/hudson/util/TextFile.java @@ -29,7 +29,7 @@ import hudson.Util; import jenkins.util.io.LinesStream; import java.nio.file.Files; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; @@ -50,9 +50,9 @@ import java.nio.charset.StandardCharsets; */ public class TextFile { - public final @Nonnull File file; + public final @NonNull File file; - public TextFile(@Nonnull File file) { + public TextFile(@NonNull File file) { this.file = file; } @@ -86,7 +86,7 @@ public class TextFile { * if the collection is not fully iterated. Use {@link #linesStream()} instead. */ @Deprecated - public @Nonnull Iterable lines() { + public @NonNull Iterable lines() { try { return linesStream(); } catch (IOException ex) { @@ -104,7 +104,7 @@ public class TextFile { * @since 2.111 */ @CreatesObligation - public @Nonnull LinesStream linesStream() throws IOException { + public @NonNull LinesStream linesStream() throws IOException { return new LinesStream(Util.fileToPath(file)); } @@ -126,7 +126,7 @@ public class TextFile { /** * Reads the first N characters or until we hit EOF. */ - public @Nonnull String head(int numChars) throws IOException { + public @NonNull String head(int numChars) throws IOException { char[] buf = new char[numChars]; int read = 0; try (Reader r = new FileReader(file)) { @@ -171,7 +171,7 @@ public class TextFile { *

* So all in all, this algorithm should work decently, and it works quite efficiently on a large text. */ - public @Nonnull String fastTail(int numChars, Charset cs) throws IOException { + public @NonNull String fastTail(int numChars, Charset cs) throws IOException { try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { long len = raf.length(); @@ -192,7 +192,7 @@ public class TextFile { /** * Uses the platform default encoding. */ - public @Nonnull String fastTail(int numChars) throws IOException { + public @NonNull String fastTail(int numChars) throws IOException { return fastTail(numChars,Charset.defaultCharset()); } diff --git a/core/src/main/java/hudson/util/XStream2.java b/core/src/main/java/hudson/util/XStream2.java index 43f0dfd08c..c86e82308e 100644 --- a/core/src/main/java/hudson/util/XStream2.java +++ b/core/src/main/java/hudson/util/XStream2.java @@ -78,8 +78,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * {@link XStream} enhanced for additional Java5 support and improved robustness. @@ -141,7 +141,7 @@ public class XStream2 extends XStream { * Even for primitive-valued fields, it is useful to guarantee * that unmarshaling will produce the same result as creating a new instance. *

Do not use in cases where the root objects defines fields (typically {@code final}) - * which it expects to be {@link Nonnull} unless you are prepared to restore default values for those fields. + * which it expects to be {@link NonNull} unless you are prepared to restore default values for those fields. * @param nullOut whether to perform this special behavior; * false to use the stock XStream behavior of leaving unmentioned {@code root} fields untouched * @see XmlFile#unmarshalNullingOut @@ -327,7 +327,7 @@ public class XStream2 extends XStream { mapperInjectionPoint.setDelegate(m); } - final class MapperInjectionPoint extends MapperDelegate { + final static class MapperInjectionPoint extends MapperDelegate { public MapperInjectionPoint(Mapper wrapped) { super(wrapped); } diff --git a/core/src/main/java/hudson/util/jna/DotNet.java b/core/src/main/java/hudson/util/jna/DotNet.java index 4d820149ca..d3a453a46c 100644 --- a/core/src/main/java/hudson/util/jna/DotNet.java +++ b/core/src/main/java/hudson/util/jna/DotNet.java @@ -30,8 +30,6 @@ import org.jinterop.winreg.JIPolicyHandle; import org.jinterop.winreg.JIWinRegFactory; import java.net.UnknownHostException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * .NET related code. @@ -39,74 +37,233 @@ import java.util.regex.Pattern; * @author Kohsuke Kawaguchi */ public class DotNet { + private static final String PATH10 = "SOFTWARE\\Microsoft\\.NETFramework\\Policy\\v1.0\\3705"; + private static final String PATH11 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v1.1.4322"; + private static final String PATH20 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v2.0.50727"; + private static final String PATH30 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.0\\Setup"; + private static final String PATH35 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5"; + private static final String PATH4 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full"; + + private static final String VALUE_INSTALL = "Install"; + private static final String VALUE_INSTALL_SUCCESS = "InstallSuccess"; + private static final String VALUE_RELEASE = "Release"; + /** - * Returns true if the .NET framework of the given version (or greater) is installed. + * Returns true if the .NET framework of a compatible version is installed. */ public static boolean isInstalled(int major, int minor) { try { - // see http://support.microsoft.com/?scid=kb;en-us;315291 for the basic algorithm - // observation in my registry shows that the actual key name can be things like "v2.0 SP1" - // or "v2.0.50727", so the regexp is written to accommodate this. - RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly("SOFTWARE\\Microsoft\\.NETFramework"); - try { - for( String keyName : key.getSubKeys() ) { - if (matches(keyName, major, minor)) - return true; - } + if (major == 4 && minor >= 5) { + return isV45PlusInstalled(minor); + } else if (major == 4 && minor == 0) { + return isV40Installed(); + } else if (major == 3 && minor == 5) { + return isV35Installed(); + } else if (major == 3 && minor == 0) { + return isV35Installed() || isV30Installed(); + } else if (major == 2 && minor == 0) { + return isV35Installed() || isV30Installed() || isV20Installed(); + } else if (major == 1 && minor == 1) { + return isV11Installed(); + } else if (major == 1 && minor == 0) { + return isV11Installed() || isV10Installed(); + } else { return false; - } finally { - key.dispose(); } } catch (JnaException e) { - if(e.getErrorCode()==2) // thrown when openReadonly fails because the key doesn't exist. + if (e.getErrorCode() == 2) { + // thrown when openReadonly fails because the key doesn't exist. return false; + } throw e; } } + private static boolean isV45PlusInstalled(int minor) { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH4)) { + return key.getIntValue(VALUE_RELEASE) >= GetV45PlusMinRelease(minor); + } + } + + private static boolean isV40Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH4)) { + return key.getIntValue(VALUE_INSTALL) == 1; + } + } + + private static boolean isV35Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH35)) { + return key.getIntValue(VALUE_INSTALL) == 1; + } + } + + private static boolean isV30Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH30)) { + return key.getIntValue(VALUE_INSTALL_SUCCESS) == 1; + } + } + + private static boolean isV20Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH20)) { + return key.getIntValue(VALUE_INSTALL) == 1; + } + } + + private static boolean isV11Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH11)) { + return key.getIntValue(VALUE_INSTALL) == 1; + } + } + + private static boolean isV10Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH10)) { + return key.getStringValue(VALUE_INSTALL) == "1"; + } + } + /** - * Returns true if the .NET framework of the given version (or grater) is installed - * on a remote machine. + * Returns true if the .NET framework of a compatible version is installed on a remote machine. */ public static boolean isInstalled(int major, int minor, String targetMachine, IJIAuthInfo session) throws JIException, UnknownHostException { - IJIWinReg registry = JIWinRegFactory.getSingleTon().getWinreg(session,targetMachine,true); - JIPolicyHandle hklm=null; - JIPolicyHandle key=null; - + IJIWinReg registry = JIWinRegFactory.getSingleTon().getWinreg(session, targetMachine, true); + JIPolicyHandle hklm = null; try { hklm = registry.winreg_OpenHKLM(); - key = registry.winreg_OpenKey(hklm,"SOFTWARE\\Microsoft\\.NETFramework", IJIWinReg.KEY_READ ); - - for( int i=0; ; i++ ) { - String keyName = registry.winreg_EnumKey(key,i)[0]; - if(matches(keyName,major,minor)) - return true; + if (major == 4 && minor >= 5) { + return isV45PlusInstalled(minor, registry, hklm); + } else if (major == 4 && minor == 0) { + return isV40Installed(registry, hklm); + } else if (major == 3 && minor == 5) { + return isV35Installed(registry, hklm); + } else if (major == 3 && minor == 0) { + return isV35Installed(registry, hklm) || isV30Installed(registry, hklm); + } else if (major == 2 && minor == 0) { + return isV35Installed(registry, hklm) || isV30Installed(registry, hklm) || isV20Installed(registry, hklm); + } else if (major == 1 && minor == 1) { + return isV11Installed(registry, hklm); + } else if (major == 1 && minor == 0) { + return isV11Installed(registry, hklm) || isV10Installed(registry, hklm); + } else { + return false; } } catch (JIException e) { - if(e.getErrorCode()==2) - return false; // not found + if (e.getErrorCode() == 2) { + // not found + return false; + } throw e; } finally { - if(hklm!=null) + if (hklm != null) { registry.winreg_CloseKey(hklm); - if(key!=null) - registry.winreg_CloseKey(key); + } registry.closeConnection(); } } - private static boolean matches(String keyName, int major, int minor) { - Matcher m = VERSION_PATTERN.matcher(keyName); - if(m.matches()) { - int mj = Integer.parseInt(m.group(1)); - if(mj>=major) { - int mn = Integer.parseInt(m.group(2)); - if(mn>=minor) - return true; + private static boolean isV45PlusInstalled(int minor, IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH4, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_RELEASE) >= GetV45PlusMinRelease(minor); + } finally { + if (key != null) { + registry.winreg_CloseKey(key); } } - return false; } - private static final Pattern VERSION_PATTERN = Pattern.compile("v(\\d+)\\.(\\d+).*"); + private static boolean isV40Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH4, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_INSTALL) == 1; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static boolean isV35Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH35, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_INSTALL) == 1; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static boolean isV30Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH30, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_INSTALL_SUCCESS) == 1; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static boolean isV20Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH20, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_INSTALL) == 1; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static boolean isV11Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH11, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_INSTALL) == 1; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static boolean isV10Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH10, IJIWinReg.KEY_READ); + return GetStringValue(registry, key, VALUE_INSTALL) == "1"; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static int GetIntValue(IJIWinReg registry, JIPolicyHandle key, String name) throws JIException { + return RegistryKey.convertBufferToInt((byte[])registry.winreg_QueryValue(key, name, Integer.BYTES)[1]); + } + + private static String GetStringValue(IJIWinReg registry, JIPolicyHandle key, String name) throws JIException { + return RegistryKey.convertBufferToString((byte[])registry.winreg_QueryValue(key, name, Character.BYTES * 2)[1]); + } + + private static int GetV45PlusMinRelease(int minor) { + switch (minor) { + case 5: + return 378389; + case 6: + return 393295; + case 7: + return 460798; + case 8: + return 528040; + default: + return Integer.MAX_VALUE; + } + } } diff --git a/core/src/main/java/hudson/util/jna/RegistryKey.java b/core/src/main/java/hudson/util/jna/RegistryKey.java index 81ed3f06b3..554a1263ec 100644 --- a/core/src/main/java/hudson/util/jna/RegistryKey.java +++ b/core/src/main/java/hudson/util/jna/RegistryKey.java @@ -27,7 +27,7 @@ import java.util.TreeSet; * * @author Kohsuke Kawaguchi */ -public class RegistryKey { +public class RegistryKey implements AutoCloseable { /** * 32bit Windows key value. */ @@ -64,7 +64,7 @@ public class RegistryKey { * @throws java.io.UnsupportedEncodingException on error * @return String */ - private static String convertBufferToString(byte[] buf) { + static String convertBufferToString(byte[] buf) { return new String(buf, 0, buf.length - 2, StandardCharsets.UTF_16LE); } @@ -74,7 +74,7 @@ public class RegistryKey { * @param buf buffer * @return int */ - private static int convertBufferToInt(byte[] buf) { + static int convertBufferToInt(byte[] buf) { return ((buf[0] & 0xff) + ((buf[1] & 0xff) << 8) + ((buf[2] & 0xff) << 16) + ((buf[3] & 0xff) << 24)); } @@ -287,6 +287,10 @@ public class RegistryKey { handle = 0; } + public void close() { + dispose(); + } + // // Root keys // diff --git a/core/src/main/java/hudson/views/MyViewsTabBar.java b/core/src/main/java/hudson/views/MyViewsTabBar.java index 90ab81cf8e..7a7c4153bb 100644 --- a/core/src/main/java/hudson/views/MyViewsTabBar.java +++ b/core/src/main/java/hudson/views/MyViewsTabBar.java @@ -32,7 +32,7 @@ import hudson.model.View; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.GlobalConfiguration; import jenkins.model.Jenkins; import hudson.model.MyViewsProperty; @@ -77,10 +77,10 @@ public abstract class MyViewsTabBar extends AbstractDescribableImpl sort(@Nonnull List views) { + public List sort(@NonNull List views) { List result = new ArrayList<>(views); result.sort(new Comparator() { @Override diff --git a/core/src/main/java/hudson/views/ViewsTabBar.java b/core/src/main/java/hudson/views/ViewsTabBar.java index 69a3ba6bb0..93aa308456 100644 --- a/core/src/main/java/hudson/views/ViewsTabBar.java +++ b/core/src/main/java/hudson/views/ViewsTabBar.java @@ -32,7 +32,7 @@ import hudson.model.View; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.GlobalConfiguration; import jenkins.model.Jenkins; import hudson.model.ListView; @@ -78,10 +78,10 @@ public abstract class ViewsTabBar extends AbstractDescribableImpl i * @return the sorted views * @since 2.37 */ - @Nonnull + @NonNull @Restricted(NoExternalUse.class) @SuppressWarnings("unused") // invoked from stapler view - public List sort(@Nonnull List views) { + public List sort(@NonNull List views) { List result = new ArrayList<>(views); result.sort(new Comparator() { @Override diff --git a/core/src/main/java/hudson/widgets/HistoryWidget.java b/core/src/main/java/hudson/widgets/HistoryWidget.java index 09fbaa250c..596fa205ee 100644 --- a/core/src/main/java/hudson/widgets/HistoryWidget.java +++ b/core/src/main/java/hudson/widgets/HistoryWidget.java @@ -35,7 +35,7 @@ import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import javax.servlet.ServletException; import java.io.IOException; import java.util.ArrayList; diff --git a/core/src/main/java/jenkins/AgentProtocol.java b/core/src/main/java/jenkins/AgentProtocol.java index 39a7f0f13a..07f8e8df9a 100644 --- a/core/src/main/java/jenkins/AgentProtocol.java +++ b/core/src/main/java/jenkins/AgentProtocol.java @@ -8,7 +8,7 @@ import hudson.TcpSlaveAgentListener; import java.io.IOException; import java.net.Socket; import java.util.Set; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import jenkins.model.Jenkins; diff --git a/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java b/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java index 895148909f..052fc7bd8f 100644 --- a/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java +++ b/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java @@ -5,8 +5,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.Enumeration; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Reflective access to various {@link ClassLoader} methods which are otherwise {@code protected}. @@ -76,7 +76,7 @@ public class ClassLoaderReflectionToolkit { * Calls {@link ClassLoader#findClass} while holding {@link ClassLoader#getClassLoadingLock}. * @since 1.553 */ - public static @Nonnull Class _findClass(ClassLoader cl, String name) throws ClassNotFoundException { + public static @NonNull Class _findClass(ClassLoader cl, String name) throws ClassNotFoundException { synchronized (getClassLoadingLock(cl, name)) { return (Class) invoke(FIND_CLASS, ClassNotFoundException.class, cl, name); } @@ -94,7 +94,7 @@ public class ClassLoaderReflectionToolkit { * Calls {@link ClassLoader#findResources}. * @since 1.553 */ - public static @Nonnull Enumeration _findResources(ClassLoader cl, String name) throws IOException { + public static @NonNull Enumeration _findResources(ClassLoader cl, String name) throws IOException { return (Enumeration) invoke(FIND_RESOURCES, IOException.class, cl, name); } diff --git a/core/src/main/java/jenkins/FilePathFilter.java b/core/src/main/java/jenkins/FilePathFilter.java index 4d4eb4f2be..01681021f8 100644 --- a/core/src/main/java/jenkins/FilePathFilter.java +++ b/core/src/main/java/jenkins/FilePathFilter.java @@ -5,7 +5,7 @@ import hudson.remoting.Channel; import hudson.remoting.ChannelBuilder; import jenkins.security.ChannelConfigurator; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import java.io.File; /** diff --git a/core/src/main/java/jenkins/FilePathFilterAggregator.java b/core/src/main/java/jenkins/FilePathFilterAggregator.java index 407d8da2cb..f338d25888 100644 --- a/core/src/main/java/jenkins/FilePathFilterAggregator.java +++ b/core/src/main/java/jenkins/FilePathFilterAggregator.java @@ -20,7 +20,7 @@ import java.util.concurrent.CopyOnWriteArrayList; class FilePathFilterAggregator extends FilePathFilter { private final CopyOnWriteArrayList all = new CopyOnWriteArrayList<>(); - private class Entry implements Comparable { + private static class Entry implements Comparable { final FilePathFilter filter; final double ordinal; diff --git a/core/src/main/java/jenkins/MetaLocaleDrivenResourceProvider.java b/core/src/main/java/jenkins/MetaLocaleDrivenResourceProvider.java index 951eb432fc..bd9ade9a05 100644 --- a/core/src/main/java/jenkins/MetaLocaleDrivenResourceProvider.java +++ b/core/src/main/java/jenkins/MetaLocaleDrivenResourceProvider.java @@ -30,8 +30,8 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.LocaleDrivenResourceProvider; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.net.URL; /** @@ -45,7 +45,7 @@ public final class MetaLocaleDrivenResourceProvider extends LocaleDrivenResource @Override @CheckForNull - public URL lookup(@Nonnull String s) { + public URL lookup(@NonNull String s) { for (PluginLocaleDrivenResourceProvider provider : ExtensionList.lookup(PluginLocaleDrivenResourceProvider.class)) { try { URL url = provider.lookup(s); diff --git a/core/src/main/java/jenkins/PluginLocaleDrivenResourceProvider.java b/core/src/main/java/jenkins/PluginLocaleDrivenResourceProvider.java index 367d664f2f..03897b67c7 100644 --- a/core/src/main/java/jenkins/PluginLocaleDrivenResourceProvider.java +++ b/core/src/main/java/jenkins/PluginLocaleDrivenResourceProvider.java @@ -25,8 +25,8 @@ package jenkins; import hudson.ExtensionPoint; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.net.URL; /** @@ -44,5 +44,5 @@ public interface PluginLocaleDrivenResourceProvider extends ExtensionPoint { * @return URL to the localized resource, or {@code null} if inapplicable. */ @CheckForNull - URL lookup(@Nonnull String s); + URL lookup(@NonNull String s); } diff --git a/core/src/main/java/jenkins/SoloFilePathFilter.java b/core/src/main/java/jenkins/SoloFilePathFilter.java index aa0ebbefc8..3324029230 100644 --- a/core/src/main/java/jenkins/SoloFilePathFilter.java +++ b/core/src/main/java/jenkins/SoloFilePathFilter.java @@ -2,7 +2,7 @@ package jenkins; import hudson.FilePath; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.Nullable; import java.io.File; /** diff --git a/core/src/main/java/jenkins/agents/AgentComputerUtil.java b/core/src/main/java/jenkins/agents/AgentComputerUtil.java new file mode 100644 index 0000000000..3aa026124e --- /dev/null +++ b/core/src/main/java/jenkins/agents/AgentComputerUtil.java @@ -0,0 +1,60 @@ +/* + * The MIT License + * + * Copyright 2020 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 jenkins.agents; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import hudson.FilePath; +import hudson.remoting.Channel; +import hudson.remoting.VirtualChannel; +import jenkins.model.Jenkins; + +public final class AgentComputerUtil { + private AgentComputerUtil() { + } + + /** + * Obtains a {@link VirtualChannel} that allows some computation to be performed on the master. + * This method can be called from any thread on the master, or from agent (more precisely, + * it only works from the remoting request-handling thread in agents, which means if you've started + * separate thread on agents, that'll fail.) + * + * @return null if the calling thread doesn't have any trace of where its master is. + * @since TODO + */ + @CheckForNull + public static VirtualChannel getChannelToMaster() { + if (Jenkins.getInstanceOrNull() != null) {// check if calling thread is on master or on agent + return FilePath.localChannel; + } + + // if this method is called from within the agent computation thread, this should work + Channel c = Channel.current(); + if (c != null && Boolean.TRUE.equals(c.getProperty("slave"))) { + return c; + } + + return null; + } +} diff --git a/core/src/main/java/jenkins/agents/WebSocketAgents.java b/core/src/main/java/jenkins/agents/WebSocketAgents.java index 965c3b67eb..4cbfd9465f 100644 --- a/core/src/main/java/jenkins/agents/WebSocketAgents.java +++ b/core/src/main/java/jenkins/agents/WebSocketAgents.java @@ -30,17 +30,16 @@ import hudson.ExtensionList; import hudson.model.Computer; import hudson.model.InvisibleAction; import hudson.model.UnprotectedRootAction; -import hudson.remoting.AbstractByteArrayCommandTransport; +import hudson.remoting.AbstractByteBufferCommandTransport; import hudson.remoting.Capability; -import hudson.remoting.Channel; import hudson.remoting.ChannelBuilder; +import hudson.remoting.ChunkHeader; import hudson.remoting.Engine; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; -import java.util.Arrays; import java.util.Collections; import java.util.logging.Level; import java.util.logging.Logger; @@ -106,7 +105,7 @@ public final class WebSocketAgents extends InvisibleAction implements Unprotecte private final JnlpConnectionState state; private final String agent; private final Capability remoteCapability; - private AbstractByteArrayCommandTransport.ByteArrayReceiver receiver; + private Transport transport; Session(JnlpConnectionState state, String agent, Capability remoteCapability) { this.state = state; @@ -119,7 +118,8 @@ public final class WebSocketAgents extends InvisibleAction implements Unprotecte Computer.threadPoolForRemoting.submit(() -> { LOGGER.fine(() -> "setting up channel for " + agent); state.fireBeforeChannel(new ChannelBuilder(agent, Computer.threadPoolForRemoting)); - state.fireAfterChannel(state.getChannelBuilder().build(new Transport())); + transport = new Transport(); + state.fireAfterChannel(state.getChannelBuilder().build(transport)); LOGGER.fine(() -> "set up channel for " + agent); return null; }); @@ -128,10 +128,10 @@ public final class WebSocketAgents extends InvisibleAction implements Unprotecte @Override protected void binary(byte[] payload, int offset, int len) { LOGGER.finest(() -> "reading block of length " + len + " from " + agent); - if (offset == 0 && len == payload.length) { - receiver.handle(payload); - } else { - receiver.handle(Arrays.copyOfRange(payload, offset, offset + len)); + try { + transport.receive(ByteBuffer.wrap(payload, offset, len)); + } catch (IOException | InterruptedException e) { + error(e); } } @@ -139,7 +139,7 @@ public final class WebSocketAgents extends InvisibleAction implements Unprotecte protected void closed(int statusCode, String reason) { LOGGER.finest(() -> "closed " + statusCode + " " + reason); IOException x = new ClosedChannelException(); - receiver.terminate(x); + transport.terminate(x); state.fireChannelClosed(x); state.fireAfterDisconnect(); } @@ -149,22 +149,13 @@ public final class WebSocketAgents extends InvisibleAction implements Unprotecte LOGGER.log(Level.WARNING, null, cause); } - class Transport extends AbstractByteArrayCommandTransport { + class Transport extends AbstractByteBufferCommandTransport { @Override - public void setup(AbstractByteArrayCommandTransport.ByteArrayReceiver bar) { - receiver = bar; - } - - @Override - public void writeBlock(Channel chnl, byte[] bytes) throws IOException { - LOGGER.finest(() -> "writing block of length " + bytes.length + " to " + agent); - try { - sendBinary(ByteBuffer.wrap(bytes)).get(); - } catch (Exception x) { - x.printStackTrace(); - throw new IOException(x); - } + protected void write(ByteBuffer header, ByteBuffer data) throws IOException { + LOGGER.finest(() -> "sending message of length " + ChunkHeader.length(ChunkHeader.peek(header))); + sendBinary(header, false); + sendBinary(data, true); } @Override @@ -183,7 +174,6 @@ public final class WebSocketAgents extends InvisibleAction implements Unprotecte LOGGER.finest(() -> "closeRead"); close(); } - } } diff --git a/core/src/main/java/jenkins/diagnostics/RootUrlNotSetMonitor.java b/core/src/main/java/jenkins/diagnostics/RootUrlNotSetMonitor.java index bb4a252409..ebf97fbd08 100644 --- a/core/src/main/java/jenkins/diagnostics/RootUrlNotSetMonitor.java +++ b/core/src/main/java/jenkins/diagnostics/RootUrlNotSetMonitor.java @@ -25,6 +25,8 @@ package jenkins.diagnostics; import hudson.Extension; import hudson.model.AdministrativeMonitor; +import hudson.security.Permission; +import jenkins.model.Jenkins; import jenkins.model.JenkinsLocationConfiguration; import jenkins.util.UrlHelper; import org.jenkinsci.Symbol; @@ -61,4 +63,9 @@ public class RootUrlNotSetMonitor extends AdministrativeMonitor { JenkinsLocationConfiguration loc = JenkinsLocationConfiguration.get(); return loc.getUrl() == null; } + + @Override + public Permission getRequiredPermission() { + return Jenkins.SYSTEM_READ; + } } diff --git a/core/src/main/java/jenkins/install/InstallState.java b/core/src/main/java/jenkins/install/InstallState.java index 19e2e360e3..bbed401f3b 100644 --- a/core/src/main/java/jenkins/install/InstallState.java +++ b/core/src/main/java/jenkins/install/InstallState.java @@ -23,8 +23,8 @@ */ package jenkins.install; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.ExtensionList; @@ -232,7 +232,7 @@ public class InstallState implements ExtensionPoint { */ private final String name; - public InstallState(@Nonnull String name, boolean isSetupComplete) { + public InstallState(@NonNull String name, boolean isSetupComplete) { this.name = name; this.isSetupComplete = isSetupComplete; } @@ -303,7 +303,7 @@ public class InstallState implements ExtensionPoint { * @return */ @CheckForNull - public static InstallState valueOf(@Nonnull String name) { + public static InstallState valueOf(@NonNull String name) { for (InstallState state : all()) { if (name.equals(state.name)) { return state; diff --git a/core/src/main/java/jenkins/install/InstallUtil.java b/core/src/main/java/jenkins/install/InstallUtil.java index be9688e894..009f727b22 100644 --- a/core/src/main/java/jenkins/install/InstallUtil.java +++ b/core/src/main/java/jenkins/install/InstallUtil.java @@ -36,8 +36,8 @@ import java.util.List; import java.util.Map; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.inject.Provider; import org.apache.commons.io.FileUtils; @@ -221,7 +221,7 @@ public class InstallUtil { * @return The last saved Jenkins instance version. * @see #saveLastExecVersion() */ - public static @Nonnull String getLastExecVersion() { + public static @NonNull String getLastExecVersion() { File lastExecVersionFile = getLastExecVersionFile(); if (lastExecVersionFile.exists()) { try { @@ -265,7 +265,7 @@ public class InstallUtil { * Save a specific version as the last execute version. * @param version The version to save. */ - static void saveLastExecVersion(@Nonnull String version) { + static void saveLastExecVersion(@NonNull String version) { File lastExecVersionFile = getLastExecVersionFile(); try { FileUtils.write(lastExecVersionFile, version, Charset.defaultCharset()); diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java index 35524fd4dd..aa2efd05ba 100644 --- a/core/src/main/java/jenkins/install/SetupWizard.java +++ b/core/src/main/java/jenkins/install/SetupWizard.java @@ -12,7 +12,7 @@ import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -24,6 +24,7 @@ import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import hudson.security.csrf.GlobalCrumbIssuerConfiguration; import jenkins.model.JenkinsLocationConfiguration; import jenkins.security.seed.UserSeedProperty; import jenkins.util.SystemProperties; @@ -52,7 +53,6 @@ import hudson.security.FullControlOnceLoggedInAuthorizationStrategy; import hudson.security.HudsonPrivateSecurityRealm; import hudson.security.SecurityRealm; import hudson.security.csrf.CrumbIssuer; -import hudson.security.csrf.DefaultCrumbIssuer; import hudson.util.HttpResponses; import hudson.util.PluginServletFilter; import hudson.util.VersionNumber; @@ -133,7 +133,7 @@ public class SetupWizard extends PageDecorator { jenkins.setSlaveAgentPort(SystemProperties.getInteger(Jenkins.class.getName()+".slaveAgentPort",-1)); // require a crumb issuer - jenkins.setCrumbIssuer(new DefaultCrumbIssuer(SystemProperties.getBoolean(Jenkins.class.getName() + ".crumbIssuerProxyCompatibility",false))); + jenkins.setCrumbIssuer(GlobalCrumbIssuerConfiguration.createDefaultCrumbIssuer()); // set master -> slave security: jenkins.getInjector().getInstance(AdminWhitelistRule.class) diff --git a/core/src/main/java/jenkins/management/AdministrativeMonitorsDecorator.java b/core/src/main/java/jenkins/management/AdministrativeMonitorsDecorator.java index a40708779e..64f7baae63 100644 --- a/core/src/main/java/jenkins/management/AdministrativeMonitorsDecorator.java +++ b/core/src/main/java/jenkins/management/AdministrativeMonitorsDecorator.java @@ -24,7 +24,6 @@ package jenkins.management; import hudson.Extension; -import hudson.Functions; import hudson.diagnosis.ReverseProxySetupMonitor; import hudson.model.AdministrativeMonitor; import hudson.model.PageDecorator; @@ -38,8 +37,6 @@ import org.kohsuke.stapler.Ancestor; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; -import javax.servlet.ServletException; -import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -91,11 +88,9 @@ public class AdministrativeMonitorsDecorator extends PageDecorator { /** * Whether the administrative monitors notifier should be shown. * @return true iff the administrative monitors notifier should be shown. - * @throws IOException - * @throws ServletException */ - public boolean shouldDisplay() throws IOException, ServletException { - if (!Functions.hasPermission(Jenkins.ADMINISTER)) { + public boolean shouldDisplay() { + if (!Jenkins.get().hasPermission(Jenkins.SYSTEM_READ)) { return false; } diff --git a/core/src/main/java/jenkins/management/AsynchronousAdministrativeMonitor.java b/core/src/main/java/jenkins/management/AsynchronousAdministrativeMonitor.java index a5e64b38f4..e7bc8c46a0 100644 --- a/core/src/main/java/jenkins/management/AsynchronousAdministrativeMonitor.java +++ b/core/src/main/java/jenkins/management/AsynchronousAdministrativeMonitor.java @@ -16,7 +16,7 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Convenient partial implementation of {@link AdministrativeMonitor} that involves a background "fixing" action @@ -120,7 +120,7 @@ public abstract class AsynchronousAdministrativeMonitor extends AdministrativeMo * Runs the monitor and encapsulates all errors within. * @since 1.590 */ - private void doRun(@Nonnull TaskListener listener) { + private void doRun(@NonNull TaskListener listener) { try { fix(listener); } catch (AbortException e) { diff --git a/core/src/main/java/jenkins/management/CliLink.java b/core/src/main/java/jenkins/management/CliLink.java index e51ba7ee03..d2ee8717ca 100644 --- a/core/src/main/java/jenkins/management/CliLink.java +++ b/core/src/main/java/jenkins/management/CliLink.java @@ -30,7 +30,7 @@ import hudson.security.Permission; import jenkins.model.Jenkins; import org.jenkinsci.Symbol; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * @author Nicolas De Loof @@ -52,7 +52,7 @@ public class CliLink extends ManagementLink { return Messages.CliLink_Description(); } - @Nonnull + @NonNull @Override public Permission getRequiredPermission() { return Jenkins.MANAGE; @@ -62,4 +62,10 @@ public class CliLink extends ManagementLink { public String getUrlName() { return "cli"; } + + @NonNull + @Override + public Category getCategory() { + return Category.TOOLS; + } } diff --git a/core/src/main/java/jenkins/management/ConfigureLink.java b/core/src/main/java/jenkins/management/ConfigureLink.java index 79637d3649..234a5527f9 100644 --- a/core/src/main/java/jenkins/management/ConfigureLink.java +++ b/core/src/main/java/jenkins/management/ConfigureLink.java @@ -30,7 +30,7 @@ import hudson.security.Permission; import jenkins.model.Jenkins; import org.jenkinsci.Symbol; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * @author Nicolas De Loof @@ -52,14 +52,20 @@ public class ConfigureLink extends ManagementLink { return Messages.ConfigureLink_Description(); } - @Nonnull + @NonNull @Override public Permission getRequiredPermission() { - return Jenkins.MANAGE; + return Jenkins.READ; } @Override public String getUrlName() { return "configure"; } + + @NonNull + @Override + public Category getCategory() { + return Category.CONFIGURATION; + } } diff --git a/core/src/main/java/jenkins/management/ConsoleLink.java b/core/src/main/java/jenkins/management/ConsoleLink.java index edd7c628cc..6e62d2c4e5 100644 --- a/core/src/main/java/jenkins/management/ConsoleLink.java +++ b/core/src/main/java/jenkins/management/ConsoleLink.java @@ -30,6 +30,8 @@ import hudson.security.Permission; import jenkins.model.Jenkins; import org.jenkinsci.Symbol; +import edu.umd.cs.findbugs.annotations.NonNull; + /** * @author Nicolas De Loof */ @@ -57,6 +59,12 @@ public class ConsoleLink extends ManagementLink { @Override public Permission getRequiredPermission() { - return Jenkins.RUN_SCRIPTS; + return Jenkins.ADMINISTER; + } + + @NonNull + @Override + public Category getCategory() { + return Category.TOOLS; } } diff --git a/core/src/main/java/jenkins/management/NodesLink.java b/core/src/main/java/jenkins/management/NodesLink.java index 57e5ebc716..6e048a62e8 100644 --- a/core/src/main/java/jenkins/management/NodesLink.java +++ b/core/src/main/java/jenkins/management/NodesLink.java @@ -27,11 +27,10 @@ package jenkins.management; import hudson.Extension; import hudson.model.ManagementLink; import hudson.security.Permission; -import jenkins.management.Messages; import jenkins.model.Jenkins; import org.jenkinsci.Symbol; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * @author Nicolas De Loof @@ -53,7 +52,7 @@ public class NodesLink extends ManagementLink { return Messages.NodesLink_Description(); } - @Nonnull + @NonNull @Override public Permission getRequiredPermission() { return Jenkins.MANAGE; @@ -63,4 +62,10 @@ public class NodesLink extends ManagementLink { public String getUrlName() { return "computer"; } + + @NonNull + @Override + public Category getCategory() { + return Category.CONFIGURATION; + } } diff --git a/core/src/main/java/jenkins/management/PluginsLink.java b/core/src/main/java/jenkins/management/PluginsLink.java index cae9c139c4..8910c62890 100644 --- a/core/src/main/java/jenkins/management/PluginsLink.java +++ b/core/src/main/java/jenkins/management/PluginsLink.java @@ -26,8 +26,12 @@ package jenkins.management; import hudson.Extension; import hudson.model.ManagementLink; +import hudson.security.Permission; +import jenkins.model.Jenkins; import org.jenkinsci.Symbol; +import edu.umd.cs.findbugs.annotations.NonNull; + /** * @author Nicolas De Loof */ @@ -52,4 +56,16 @@ public class PluginsLink extends ManagementLink { public String getUrlName() { return "pluginManager"; } + + @NonNull + @Override + public Permission getRequiredPermission() { + return Jenkins.SYSTEM_READ; + } + + @NonNull + @Override + public Category getCategory() { + return Category.CONFIGURATION; + } } diff --git a/core/src/main/java/jenkins/management/ReloadLink.java b/core/src/main/java/jenkins/management/ReloadLink.java index 59fabb8ba1..7d229bb9c3 100644 --- a/core/src/main/java/jenkins/management/ReloadLink.java +++ b/core/src/main/java/jenkins/management/ReloadLink.java @@ -28,6 +28,8 @@ import hudson.Extension; import hudson.model.ManagementLink; import org.jenkinsci.Symbol; +import edu.umd.cs.findbugs.annotations.NonNull; + /** * @author Nicolas De Loof */ @@ -61,4 +63,10 @@ public class ReloadLink extends ManagementLink { public boolean getRequiresPOST() { return true; } + + @NonNull + @Override + public Category getCategory() { + return Category.TOOLS; + } } diff --git a/core/src/main/java/jenkins/management/ShutdownLink.java b/core/src/main/java/jenkins/management/ShutdownLink.java index dde06e1290..36f5f4c713 100644 --- a/core/src/main/java/jenkins/management/ShutdownLink.java +++ b/core/src/main/java/jenkins/management/ShutdownLink.java @@ -26,9 +26,12 @@ package jenkins.management; import hudson.Extension; import hudson.model.ManagementLink; +import hudson.security.Permission; import jenkins.model.Jenkins; import org.jenkinsci.Symbol; +import edu.umd.cs.findbugs.annotations.NonNull; + /** * @author Nicolas De Loof */ @@ -58,4 +61,16 @@ public class ShutdownLink extends ManagementLink { public boolean getRequiresPOST() { return true; } + + @NonNull + @Override + public Permission getRequiredPermission() { + return Jenkins.MANAGE; + } + + @NonNull + @Override + public Category getCategory() { + return Category.TOOLS; + } } diff --git a/core/src/main/java/jenkins/management/StatisticsLink.java b/core/src/main/java/jenkins/management/StatisticsLink.java index b0aa5c499c..2fa557a014 100644 --- a/core/src/main/java/jenkins/management/StatisticsLink.java +++ b/core/src/main/java/jenkins/management/StatisticsLink.java @@ -30,7 +30,7 @@ import hudson.security.Permission; import jenkins.model.Jenkins; import org.jenkinsci.Symbol; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * @author Nicolas De Loof @@ -52,7 +52,7 @@ public class StatisticsLink extends ManagementLink { return Messages.StatisticsLink_Description(); } - @Nonnull + @NonNull @Override public Permission getRequiredPermission() { return Jenkins.MANAGE; @@ -62,4 +62,10 @@ public class StatisticsLink extends ManagementLink { public String getUrlName() { return "load-statistics"; } + + @NonNull + @Override + public Category getCategory() { + return Category.STATUS; + } } diff --git a/core/src/main/java/jenkins/management/SystemInfoLink.java b/core/src/main/java/jenkins/management/SystemInfoLink.java index c45ce41de8..c9c7c5e09e 100644 --- a/core/src/main/java/jenkins/management/SystemInfoLink.java +++ b/core/src/main/java/jenkins/management/SystemInfoLink.java @@ -26,6 +26,9 @@ package jenkins.management; import hudson.Extension; import hudson.model.ManagementLink; +import hudson.security.Permission; +import edu.umd.cs.findbugs.annotations.NonNull; +import jenkins.model.Jenkins; import org.jenkinsci.Symbol; /** @@ -48,8 +51,21 @@ public class SystemInfoLink extends ManagementLink { return Messages.SystemInfoLink_Description(); } + @NonNull + @Override + public Permission getRequiredPermission() { + //This link is displayed to any user with permission to access the management menu + return Jenkins.READ; + } + @Override public String getUrlName() { return "systemInfo"; } + + @NonNull + @Override + public Category getCategory() { + return Category.STATUS; + } } diff --git a/core/src/main/java/jenkins/management/SystemLogLink.java b/core/src/main/java/jenkins/management/SystemLogLink.java index 1ee08aca00..35fadad9fd 100644 --- a/core/src/main/java/jenkins/management/SystemLogLink.java +++ b/core/src/main/java/jenkins/management/SystemLogLink.java @@ -26,8 +26,12 @@ package jenkins.management; import hudson.Extension; import hudson.model.ManagementLink; +import hudson.security.Permission; +import jenkins.model.Jenkins; import org.jenkinsci.Symbol; +import edu.umd.cs.findbugs.annotations.NonNull; + /** * @author Nicolas De Loof */ @@ -52,4 +56,16 @@ public class SystemLogLink extends ManagementLink { public String getUrlName() { return "log"; } + + @NonNull + @Override + public Permission getRequiredPermission() { + return Jenkins.SYSTEM_READ; + } + + @NonNull + @Override + public Category getCategory() { + return Category.STATUS; + } } diff --git a/core/src/main/java/jenkins/model/ArtifactManager.java b/core/src/main/java/jenkins/model/ArtifactManager.java index 874d4e3a58..b4669ed5e8 100644 --- a/core/src/main/java/jenkins/model/ArtifactManager.java +++ b/core/src/main/java/jenkins/model/ArtifactManager.java @@ -33,7 +33,7 @@ import hudson.model.TaskListener; import hudson.tasks.ArtifactArchiver; import java.io.IOException; import java.util.Map; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.util.VirtualFile; /** @@ -48,7 +48,7 @@ public abstract class ArtifactManager { * The selected manager will be persisted inside a build, so the build reference should be {@code transient} (quasi-{@code final}) and restored here. * @param build a historical build with which this manager was associated */ - public abstract void onLoad(@Nonnull Run build); + public abstract void onLoad(@NonNull Run build); /** * Archive all configured artifacts from a build. diff --git a/core/src/main/java/jenkins/model/ArtifactManagerConfiguration.java b/core/src/main/java/jenkins/model/ArtifactManagerConfiguration.java index 3b5ce11b8e..8dc41f13a8 100644 --- a/core/src/main/java/jenkins/model/ArtifactManagerConfiguration.java +++ b/core/src/main/java/jenkins/model/ArtifactManagerConfiguration.java @@ -32,7 +32,7 @@ import net.sf.json.JSONObject; import org.jenkinsci.Symbol; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * List of configured {@link ArtifactManagerFactory}s. @@ -41,7 +41,7 @@ import javax.annotation.Nonnull; @Extension @Symbol("artifactManager") public class ArtifactManagerConfiguration extends GlobalConfiguration implements PersistentDescriptor { - public static @Nonnull ArtifactManagerConfiguration get() { + public static @NonNull ArtifactManagerConfiguration get() { return GlobalConfiguration.all().getInstance(ArtifactManagerConfiguration.class); } diff --git a/core/src/main/java/jenkins/model/ArtifactManagerFactory.java b/core/src/main/java/jenkins/model/ArtifactManagerFactory.java index efff30c565..ebb8708e96 100644 --- a/core/src/main/java/jenkins/model/ArtifactManagerFactory.java +++ b/core/src/main/java/jenkins/model/ArtifactManagerFactory.java @@ -27,7 +27,7 @@ package jenkins.model; import hudson.ExtensionPoint; import hudson.model.AbstractDescribableImpl; import hudson.model.Run; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import org.kohsuke.stapler.DataBoundConstructor; /** diff --git a/core/src/main/java/jenkins/model/AssetManager.java b/core/src/main/java/jenkins/model/AssetManager.java index 4a7513c8b2..a688f0f718 100644 --- a/core/src/main/java/jenkins/model/AssetManager.java +++ b/core/src/main/java/jenkins/model/AssetManager.java @@ -9,8 +9,8 @@ import org.jenkinsci.Symbol; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -78,7 +78,7 @@ public class AssetManager implements UnprotectedRootAction { * look for child classloader first. But to support plugins that get split, if the child classloader * doesn't find it, fall back to the parent classloader. */ - private @CheckForNull URL findResource(@Nonnull String path) throws IOException { + private @CheckForNull URL findResource(@NonNull String path) throws IOException { if (StringUtils.isBlank(path)) { return null; } diff --git a/core/src/main/java/jenkins/model/BlockedBecauseOfBuildInProgress.java b/core/src/main/java/jenkins/model/BlockedBecauseOfBuildInProgress.java index 9abdd8fa88..2710667a6d 100644 --- a/core/src/main/java/jenkins/model/BlockedBecauseOfBuildInProgress.java +++ b/core/src/main/java/jenkins/model/BlockedBecauseOfBuildInProgress.java @@ -28,7 +28,7 @@ import hudson.model.Executor; import hudson.model.Job; import hudson.model.Run; import hudson.model.queue.CauseOfBlockage; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Indicates that a new build is blocked because the previous build is already in progress. @@ -37,14 +37,14 @@ import javax.annotation.Nonnull; */ public class BlockedBecauseOfBuildInProgress extends CauseOfBlockage { - @Nonnull + @NonNull private final Run build; /** * Creates a cause for the specified build. * @param build Build, which is already in progress */ - public BlockedBecauseOfBuildInProgress(@Nonnull Run build) { + public BlockedBecauseOfBuildInProgress(@NonNull Run build) { this.build = build; } diff --git a/core/src/main/java/jenkins/model/CauseOfInterruption.java b/core/src/main/java/jenkins/model/CauseOfInterruption.java index cce7987fc1..38b12cc86f 100644 --- a/core/src/main/java/jenkins/model/CauseOfInterruption.java +++ b/core/src/main/java/jenkins/model/CauseOfInterruption.java @@ -33,8 +33,8 @@ import org.kohsuke.stapler.export.ExportedBean; import java.io.Serializable; import java.util.Collections; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Records why an {@linkplain Executor#interrupt() executor is interrupted}. @@ -77,14 +77,14 @@ public abstract class CauseOfInterruption implements Serializable { */ public static final class UserInterruption extends CauseOfInterruption { - @Nonnull + @NonNull private final String user; - public UserInterruption(@Nonnull User user) { + public UserInterruption(@NonNull User user) { this.user = user.getId(); } - public UserInterruption(@Nonnull String userId) { + public UserInterruption(@NonNull String userId) { this.user = userId; } @@ -93,7 +93,7 @@ public abstract class CauseOfInterruption implements Serializable { * @return User ID * @since 2.31 */ - @Nonnull + @NonNull public String getUserId() { return user; } @@ -103,7 +103,7 @@ public abstract class CauseOfInterruption implements Serializable { * @return User instance if it can be located. * Result of {@link User#getUnknown()} otherwise */ - @Nonnull + @NonNull public User getUser() { final User userInstance = getUserOrNull(); return userInstance != null ? userInstance : User.getUnknown(); diff --git a/core/src/main/java/jenkins/model/FingerprintFacet.java b/core/src/main/java/jenkins/model/FingerprintFacet.java index 90f761ec89..c6b25e4fc3 100644 --- a/core/src/main/java/jenkins/model/FingerprintFacet.java +++ b/core/src/main/java/jenkins/model/FingerprintFacet.java @@ -30,7 +30,7 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import java.util.List; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Plugin-specific additions to fingerprint information. @@ -67,7 +67,7 @@ public abstract class FingerprintFacet implements ExtensionPoint { * @param timestamp * Timestamp when the use happened (when the facet has been created). */ - protected FingerprintFacet(@Nonnull Fingerprint fingerprint, long timestamp) { + protected FingerprintFacet(@NonNull Fingerprint fingerprint, long timestamp) { assert fingerprint!=null; this.fingerprint = fingerprint; this.timestamp = timestamp; @@ -79,7 +79,7 @@ public abstract class FingerprintFacet implements ExtensionPoint { * @return * always non-null. */ - public @Nonnull Fingerprint getFingerprint() { + public @NonNull Fingerprint getFingerprint() { return fingerprint; } @@ -103,6 +103,15 @@ public abstract class FingerprintFacet implements ExtensionPoint { return timestamp; } + /** + * Returns whether Fingerprint deletion has been blocked by this Facet. + * Returns false by default. Override the default to block the deletion of the associated Fingerprint. + * @since 2.223 + */ + public boolean isFingerprintDeletionBlocked() { + return false; + } + /** * Backdoor for {@link Fingerprint} to set itself to its facets. * Public only because this needs to be accessible to {@link Fingerprint}. Do not call this method directly. diff --git a/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java b/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java index 9b0ca5aaf8..8a1d5cf8c8 100644 --- a/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java @@ -48,6 +48,10 @@ public class GlobalBuildDiscarderConfiguration extends GlobalConfiguration { return ExtensionList.lookupSingleton(GlobalBuildDiscarderConfiguration.class); } + public GlobalBuildDiscarderConfiguration() { + load(); + } + private final DescribableList configuredBuildDiscarders = new DescribableList<>(this, Collections.singletonList(new JobGlobalBuildDiscarderStrategy())); diff --git a/core/src/main/java/jenkins/model/GlobalCloudConfiguration.java b/core/src/main/java/jenkins/model/GlobalCloudConfiguration.java index 9ac2222ad5..4ba11f5c0c 100644 --- a/core/src/main/java/jenkins/model/GlobalCloudConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalCloudConfiguration.java @@ -14,7 +14,7 @@ import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.verb.POST; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import javax.servlet.ServletException; import java.io.IOException; diff --git a/core/src/main/java/jenkins/model/GlobalConfiguration.java b/core/src/main/java/jenkins/model/GlobalConfiguration.java index 4a261cbed5..dea390902f 100644 --- a/core/src/main/java/jenkins/model/GlobalConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalConfiguration.java @@ -7,7 +7,7 @@ import hudson.model.Descriptor; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Convenient base class for extensions that contributes to the system configuration page but nothing @@ -71,7 +71,7 @@ public abstract class GlobalConfiguration extends Descriptor all() { + public static @NonNull ExtensionList all() { return Jenkins.get().getDescriptorList(GlobalConfiguration.class); // pointless type parameters help work around bugs in javac in earlier versions http://codepad.org/m1bbFRrH } diff --git a/core/src/main/java/jenkins/model/GlobalConfigurationCategory.java b/core/src/main/java/jenkins/model/GlobalConfigurationCategory.java index 5164f171e4..6805414d1f 100644 --- a/core/src/main/java/jenkins/model/GlobalConfigurationCategory.java +++ b/core/src/main/java/jenkins/model/GlobalConfigurationCategory.java @@ -6,7 +6,7 @@ import hudson.ExtensionPoint; import hudson.model.ModelObject; import org.jenkinsci.Symbol; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Grouping of related {@link GlobalConfiguration}s. @@ -41,7 +41,7 @@ public abstract class GlobalConfigurationCategory implements ExtensionPoint, Mod return ExtensionList.lookup(GlobalConfigurationCategory.class); } - public static @Nonnull T get(Class type) { + public static @NonNull T get(Class type) { T category = all().get(type); if(category == null){ throw new AssertionError("Category not found. It seems the " + type + " is not annotated with @Extension and so not registered"); diff --git a/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java b/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java index f4af422543..3ded68b173 100644 --- a/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java @@ -31,7 +31,7 @@ import net.sf.json.JSONObject; import org.jenkinsci.Symbol; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Configures the project naming strategy. @@ -63,7 +63,7 @@ public class GlobalProjectNamingStrategyConfiguration extends GlobalConfiguratio return true; } - @Nonnull + @NonNull @Override public Permission getRequiredGlobalConfigPagePermission() { return Jenkins.MANAGE; diff --git a/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java b/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java index 92a463e733..a7035f5f86 100644 --- a/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java @@ -29,7 +29,7 @@ import net.sf.json.JSONObject; import org.jenkinsci.Symbol; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; /** @@ -60,7 +60,7 @@ public class GlobalQuietPeriodConfiguration extends GlobalConfiguration { } } - @Nonnull + @NonNull @Override public Permission getRequiredGlobalConfigPagePermission() { return Jenkins.MANAGE; diff --git a/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java b/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java index 3ec65ff2e3..a5ed8681a9 100644 --- a/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java @@ -30,7 +30,7 @@ import net.sf.json.JSONObject; import org.jenkinsci.Symbol; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; /** @@ -57,7 +57,7 @@ public class GlobalSCMRetryCountConfiguration extends GlobalConfiguration { } } - @Nonnull + @NonNull @Override public Permission getRequiredGlobalConfigPagePermission() { return Jenkins.MANAGE; diff --git a/core/src/main/java/jenkins/model/IdStrategy.java b/core/src/main/java/jenkins/model/IdStrategy.java index e434343f74..673987eb9a 100644 --- a/core/src/main/java/jenkins/model/IdStrategy.java +++ b/core/src/main/java/jenkins/model/IdStrategy.java @@ -34,7 +34,7 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.ProtectedExternally; import org.kohsuke.stapler.DataBoundConstructor; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.Serializable; import java.util.Comparator; import java.util.Locale; @@ -66,7 +66,7 @@ public abstract class IdStrategy extends AbstractDescribableImpl imp * @deprecated No current use. */ @Deprecated - public String filenameOf(@Nonnull String id) { + public String filenameOf(@NonNull String id) { return null; } @@ -79,7 +79,7 @@ public abstract class IdStrategy extends AbstractDescribableImpl imp */ @Deprecated @Restricted(ProtectedExternally.class) - public String legacyFilenameOf(@Nonnull String id) { + public String legacyFilenameOf(@NonNull String id) { return null; } @@ -92,7 +92,7 @@ public abstract class IdStrategy extends AbstractDescribableImpl imp * @deprecated Use only for migrating to new format. After the migration an id is no longer represented by a filename (directory). */ @Deprecated - public String idFromFilename(@Nonnull String filename) { + public String idFromFilename(@NonNull String filename) { return filename; } @@ -103,8 +103,8 @@ public abstract class IdStrategy extends AbstractDescribableImpl imp * @param id the id. * @return the key. */ - @Nonnull - public String keyFor(@Nonnull String id) { + @NonNull + public String keyFor(@NonNull String id) { return id; } @@ -117,7 +117,7 @@ public abstract class IdStrategy extends AbstractDescribableImpl imp * @param id2 the second id. * @return {@code true} if and only if the two ids are the same. */ - public boolean equals(@Nonnull String id1, @Nonnull String id2) { + public boolean equals(@NonNull String id1, @NonNull String id2) { return compare(id1, id2) == 0; } @@ -131,7 +131,7 @@ public abstract class IdStrategy extends AbstractDescribableImpl imp * @return the sorting order of the two IDs. */ @Override - public abstract int compare(@Nonnull String id1, @Nonnull String id2); + public abstract int compare(@NonNull String id1, @NonNull String id2); @Override @SuppressWarnings("unchecked") @@ -167,8 +167,8 @@ public abstract class IdStrategy extends AbstractDescribableImpl imp return Jenkins.get().getDescriptorList(IdStrategy.class); } - String applyPatternRepeatedly(@Nonnull Pattern pattern, @Nonnull String filename, - @Nonnull Function converter) { + String applyPatternRepeatedly(@NonNull Pattern pattern, @NonNull String filename, + @NonNull Function converter) { StringBuilder id = new StringBuilder(); int beginIndex = 0; Matcher matcher = pattern.matcher(filename); @@ -197,26 +197,26 @@ public abstract class IdStrategy extends AbstractDescribableImpl imp public CaseInsensitive() {} @Override - public String idFromFilename(@Nonnull String filename) { + public String idFromFilename(@NonNull String filename) { String id = applyPatternRepeatedly(PSEUDO_UNICODE_PATTERN, filename, this::convertPseudoUnicode); return id.toLowerCase(Locale.ENGLISH); } @Override - @Nonnull - public String keyFor(@Nonnull String id) { + @NonNull + public String keyFor(@NonNull String id) { return id.toLowerCase(Locale.ENGLISH); } @Override - public int compare(@Nonnull String id1, @Nonnull String id2) { + public int compare(@NonNull String id1, @NonNull String id2) { return CaseInsensitiveComparator.INSTANCE.compare(id1, id2); } @Extension @Symbol("caseInsensitive") public static class DescriptorImpl extends IdStrategyDescriptor { - @Nonnull + @NonNull @Override public String getDisplayName() { return Messages.IdStrategy_CaseInsensitive_DisplayName(); @@ -235,7 +235,7 @@ public abstract class IdStrategy extends AbstractDescribableImpl imp public CaseSensitive() {} @Override - public String idFromFilename(@Nonnull String filename) { + public String idFromFilename(@NonNull String filename) { String id = applyPatternRepeatedly(CAPITALIZATION_PATTERN, filename, this::convertCapitalizedAscii); return applyPatternRepeatedly(PSEUDO_UNICODE_PATTERN, id, this::convertPseudoUnicode); } @@ -245,12 +245,12 @@ public abstract class IdStrategy extends AbstractDescribableImpl imp } @Override - public boolean equals(@Nonnull String id1, @Nonnull String id2) { + public boolean equals(@NonNull String id1, @NonNull String id2) { return StringUtils.equals(id1, id2); } @Override - public int compare(@Nonnull String id1, @Nonnull String id2) { + public int compare(@NonNull String id1, @NonNull String id2) { return id1.compareTo(id2); } @@ -281,20 +281,20 @@ public abstract class IdStrategy extends AbstractDescribableImpl imp public CaseSensitiveEmailAddress() {} @Override - public boolean equals(@Nonnull String id1, @Nonnull String id2) { + public boolean equals(@NonNull String id1, @NonNull String id2) { return StringUtils.equals(keyFor(id1), keyFor(id2)); } @Override - @Nonnull - public String keyFor(@Nonnull String id) { + @NonNull + public String keyFor(@NonNull String id) { int index = id.lastIndexOf('@'); // The @ can be used in local-part if quoted correctly // => the last @ is the one used to separate the domain and local-part return index == -1 ? id : id.substring(0, index) + (id.substring(index).toLowerCase(Locale.ENGLISH)); } @Override - public int compare(@Nonnull String id1, @Nonnull String id2) { + public int compare(@NonNull String id1, @NonNull String id2) { return keyFor(id1).compareTo(keyFor(id2)); } diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index e9d982249f..36ce41cbf6 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -37,6 +37,7 @@ import com.thoughtworks.xstream.XStream; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.*; import hudson.Launcher.LocalLauncher; +import hudson.security.csrf.GlobalCrumbIssuerConfiguration; import jenkins.AgentProtocol; import jenkins.diagnostics.URICheckEncodingMonitor; import jenkins.security.stapler.DoActionFilter; @@ -248,9 +249,9 @@ import org.kohsuke.stapler.jelly.JellyRequestDispatcher; import org.kohsuke.stapler.verb.POST; import org.xml.sax.InputSource; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import javax.crypto.SecretKey; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; @@ -304,7 +305,6 @@ import static hudson.init.InitMilestone.*; import hudson.init.Initializer; import hudson.util.LogTaskListener; import static java.util.logging.Level.*; -import javax.annotation.Nonnegative; import static javax.servlet.http.HttpServletResponse.*; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.stapler.WebMethod; @@ -654,7 +654,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve /** * {@link hudson.security.csrf.CrumbIssuer} */ - private volatile CrumbIssuer crumbIssuer; + private volatile CrumbIssuer crumbIssuer = GlobalCrumbIssuerConfiguration.createDefaultCrumbIssuer(); /** * All labels known to Jenkins. This allows us to reuse the same label instances @@ -770,7 +770,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * @throws IllegalStateException for the reasons that {@link #getInstanceOrNull} might return null * @since 2.98 */ - @Nonnull + @NonNull public static Jenkins get() throws IllegalStateException { Jenkins instance = getInstanceOrNull(); if (instance == null) { @@ -784,7 +784,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * @since 1.590 */ @Deprecated - @Nonnull + @NonNull public static Jenkins getActiveInstance() throws IllegalStateException { return get(); } @@ -1059,7 +1059,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * Retrieve the proxy configuration. * * @return the proxy configuration - * @since TODO + * @since 2.205 */ @CheckForNull public ProxyConfiguration getProxy() { @@ -1070,7 +1070,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * Set the proxy configuration. * * @param proxy the proxy to set - * @since TODO + * @since 2.205 */ public void setProxy(@CheckForNull ProxyConfiguration proxy) { this.proxy = proxy; @@ -1080,7 +1080,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * Get the Jenkins {@link jenkins.install.InstallState install state}. * @return The Jenkins {@link jenkins.install.InstallState install state}. */ - @Nonnull + @NonNull public InstallState getInstallState() { if (installState != null) { installStateName = installState.name(); @@ -1094,7 +1094,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * Update the current install state. This will invoke state.initializeState() * when the state has been transitioned. */ - public void setInstallState(@Nonnull InstallState newState) { + public void setInstallState(@NonNull InstallState newState) { String prior = installStateName; installStateName = newState.name(); LOGGER.log(Main.isDevelopmentMode ? Level.INFO : Level.FINE, "Install state transitioning from: {0} to : {1}", new Object[] { prior, installStateName }); @@ -1362,7 +1362,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve return systemMessage; } - @Nonnull + @NonNull public PluginManager getPluginManager() { return pluginManager; } @@ -1371,10 +1371,32 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve return updateCenter; } + /** + * If usage statistics has been disabled + * + * @since 2.226 + */ + @CheckForNull + public Boolean isNoUsageStatistics() { + return noUsageStatistics; + } + + /** + * If usage statistics are being collected + * + * @return {@code true} if usage statistics should be collected. + * Defaults to {@code true} when {@link #noUsageStatistics} is not set. + * @since 2.226 + */ public boolean isUsageStatisticsCollected() { return noUsageStatistics==null || !noUsageStatistics; } + /** + * Sets the noUsageStatistics flag + * + * @since 2.226 + */ public void setNoUsageStatistics(Boolean noUsageStatistics) throws IOException { this.noUsageStatistics = noUsageStatistics; save(); @@ -1573,7 +1595,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * If the descriptor is missing. * @since 1.326 */ - @Nonnull + @NonNull public Descriptor getDescriptorOrDie(Class type) { Descriptor d = getDescriptor(type); if (d==null) @@ -1688,7 +1710,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * never null. * @since 1.391 */ - public @Nonnull MarkupFormatter getMarkupFormatter() { + public @NonNull MarkupFormatter getMarkupFormatter() { MarkupFormatter f = markupFormatter; return f != null ? f : new EscapedMarkupFormatter(); } @@ -1770,7 +1792,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * Gets just the immediate children of {@link Jenkins} based on supplied predicate. * * @see #getAllItems(Class) - * @since TODO + * @since 2.221 */ public List getItems(Predicate pred) { List viewableItems = new ArrayList<>(); @@ -1797,9 +1819,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve */ public List getItems(Class type) { List r = new ArrayList<>(); - for (TopLevelItem i : getItems()) - if (type.isInstance(i)) - r.add(type.cast(i)); + for (TopLevelItem i : getItems(type::isInstance)) { + r.add(type.cast(i)); + } return r; } @@ -1908,7 +1930,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve return viewGroupMixIn.getPrimaryView(); } - public void setPrimaryView(@Nonnull View v) { + public void setPrimaryView(@NonNull View v) { this.primaryView = v.getViewName(); } @@ -1971,7 +1993,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } @CLIResolver - public @CheckForNull Computer getComputer(@Argument(required=true,metaVar="NAME",usage="Node name") @Nonnull String name) { + public @CheckForNull Computer getComputer(@Argument(required=true,metaVar="NAME",usage="Node name") @NonNull String name) { if(name.equals("(master)")) name = ""; @@ -1998,6 +2020,8 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve // non-existent try { + // For the record, this method creates temporary labels but there is a periodic task + // calling "trimLabels" to remove unused labels running every 5 minutes. labels.putIfAbsent(expr,Label.parseExpression(expr)); } catch (ANTLRException e) { // laxly accept it as a single label atom for backward compatibility @@ -2020,6 +2044,8 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve // non-existent LabelAtom la = new LabelAtom(name); + // For the record, this method creates temporary labels but there is a periodic task + // calling "trimLabels" to remove unused labels running every 5 minutes. if (labels.putIfAbsent(name, la)==null) la.load(); } @@ -2111,7 +2137,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * Returns all {@link Node}s in the system, excluding {@link Jenkins} instance itself which * represents the master. */ - @Nonnull + @NonNull public List getNodes() { return nodes.getNodes(); } @@ -2137,7 +2163,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve /** * Removes a {@link Node} from Jenkins. */ - public void removeNode(@Nonnull Node n) throws IOException { + public void removeNode(@NonNull Node n) throws IOException { nodes.removeNode(n); } @@ -2196,16 +2222,17 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } /** - * Returns the enabled and activated administrative monitors. + * Returns the enabled and activated administrative monitors accessible to the current user. + * * @since 2.64 */ public List getActiveAdministrativeMonitors() { - if (!Jenkins.get().hasPermission(ADMINISTER)) { + if (!Jenkins.get().hasPermission(SYSTEM_READ)) { return Collections.emptyList(); } return administrativeMonitors.stream().filter(m -> { try { - return m.isEnabled() && m.isActivated(); + return Jenkins.get().hasPermission(m.getRequiredPermission()) && m.isEnabled() && m.isActivated(); } catch (Throwable x) { LOGGER.log(Level.WARNING, null, x); return false; @@ -2282,7 +2309,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve builder.add(new CollectionSearchIndex() { protected SearchItem get(String key) { return getItemByFullName(key, TopLevelItem.class); } protected Collection all() { return getAllItems(TopLevelItem.class); } - @Nonnull + @NonNull @Override protected Iterable allAsIterable() { return allItems(TopLevelItem.class); @@ -2378,7 +2405,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * shows some examples of configuration. * @since 1.263 */ - public @Nonnull String getRootUrlFromRequest() { + public @NonNull String getRootUrlFromRequest() { StaplerRequest req = Stapler.getCurrentRequest(); if (req == null) { throw new IllegalStateException("cannot call getRootUrlFromRequest from outside a request handling thread"); @@ -2503,7 +2530,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve this.buildsDir = buildsDir; } - @Override public @Nonnull FilePath getRootPath() { + @Override public @NonNull FilePath getRootPath() { return new FilePath(getRootDir()); } @@ -2532,7 +2559,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * Everything below here is admin-only, so do the check here. */ public LogRecorderManager getLog() { - checkPermission(ADMINISTER); + checkPermission(SYSTEM_READ); return log; } @@ -2580,7 +2607,12 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve return securityRealm; } - public void setSecurityRealm(SecurityRealm securityRealm) { + /** + * Sets a security realm. + * @param securityRealm Security realm to set. + * If {@code null}, {@link SecurityRealm#NO_AUTHENTICATION} will be set. + */ + public void setSecurityRealm(@CheckForNull SecurityRealm securityRealm) { if(securityRealm==null) securityRealm= SecurityRealm.NO_AUTHENTICATION; this.useSecurity = true; @@ -2610,7 +2642,12 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve saveQuietly(); } - public void setAuthorizationStrategy(AuthorizationStrategy a) { + /** + * Sets a new authorization strategy. + * @param a Authorization strategy to set. + * If {@code null}, {@link AuthorizationStrategy#UNSECURED} will be set + */ + public void setAuthorizationStrategy(@CheckForNull AuthorizationStrategy a) { if (a == null) a = AuthorizationStrategy.UNSECURED; useSecurity = true; @@ -2687,7 +2724,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * Can be an empty list but never null. */ @SuppressWarnings({"unchecked"}) - public @Nonnull ,D extends Descriptor> DescriptorExtensionList getDescriptorList(Class type) { + public @NonNull ,D extends Descriptor> DescriptorExtensionList getDescriptorList(Class type) { return descriptorLists.computeIfAbsent(type, key -> DescriptorExtensionList.createDescriptorList(this, key)); } @@ -2795,7 +2832,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * @throws IOException Failed to save the configuration * @throws IllegalArgumentException Negative value has been passed */ - public void setNumExecutors(@Nonnegative int n) throws IOException, IllegalArgumentException { + public void setNumExecutors(/* @javax.annotation.Nonnegative*/ int n) throws IOException, IllegalArgumentException { if (n < 0) { throw new IllegalArgumentException("Incorrect field \"# of executors\": " + n +". It should be a non-negative number."); } @@ -2890,7 +2927,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve return getItem(pathName,context!=null?context.getParent():null); } - public final T getItem(String pathName, ItemGroup context, @Nonnull Class type) { + public final T getItem(String pathName, ItemGroup context, @NonNull Class type) { Item r = getItem(pathName, context); if (type.isInstance(r)) return type.cast(r); @@ -2919,7 +2956,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * or it exists but it's no an instance of the given type. * @throws AccessDeniedException as per {@link ItemGroup#getItem} */ - public @CheckForNull T getItemByFullName(@Nonnull String fullName, Class type) throws AccessDeniedException { + public @CheckForNull T getItemByFullName(@NonNull String fullName, Class type) throws AccessDeniedException { StringTokenizer tokens = new StringTokenizer(fullName,"/"); ItemGroup parent = this; @@ -3095,7 +3132,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } @Override - @Nonnull + @NonNull public Computer createComputer() { return new Hudson.MasterComputer(); } @@ -3298,9 +3335,8 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } } - - // Initialize the filter with the crumb issuer - setCrumbIssuer(crumbIssuer); + // Allow the disabling system property to interfere here + setCrumbIssuer(getCrumbIssuer()); // auto register root actions for (Action a : getExtensionList(RootAction.class)) @@ -3794,7 +3830,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve */ @CheckForNull public CrumbIssuer getCrumbIssuer() { - return crumbIssuer; + return GlobalCrumbIssuerConfiguration.DISABLE_CSRF_PROTECTION ? null : crumbIssuer; } public void setCrumbIssuer(CrumbIssuer issuer) { @@ -3862,7 +3898,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve @RequirePOST public HttpRedirect doQuietDown(@QueryParameter boolean block, @QueryParameter int timeout) throws InterruptedException, IOException { synchronized (this) { - checkPermission(ADMINISTER); + checkPermission(MANAGE); isQuietingDown = true; } if (block) { @@ -3880,9 +3916,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve /** * Cancel previous quiet down Jenkins - preparation for a restart */ - @RequirePOST // TODO the cancel link needs to be updated accordingly + @RequirePOST public synchronized HttpRedirect doCancelQuietDown() { - checkPermission(ADMINISTER); + checkPermission(MANAGE); isQuietingDown = false; getQueue().scheduleMaintenance(); return new HttpRedirect("."); @@ -4431,7 +4467,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * Gets the {@link Authentication} object that represents the user * associated with the current request. */ - public static @Nonnull Authentication getAuthentication() { + public static @NonNull Authentication getAuthentication() { Authentication a = SecurityContextHolder.getContext().getAuthentication(); // on Tomcat while serving the login page, this is null despite the fact // that we have filters. Looking at the stack trace, Tomcat doesn't seem to @@ -4462,7 +4498,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve */ public static void _doScript(StaplerRequest req, StaplerResponse rsp, RequestDispatcher view, VirtualChannel channel, ACL acl) throws IOException, ServletException { // ability to run arbitrary script is dangerous - acl.checkPermission(RUN_SCRIPTS); + acl.checkPermission(ADMINISTER); String text = req.getParameter("script"); if (text != null) { @@ -4492,7 +4528,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve */ @RequirePOST public void doEval(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - checkPermission(RUN_SCRIPTS); + checkPermission(ADMINISTER); req.getWebApp().getDispatchValidator().allowDispatch(req, rsp); try { MetaClass mc = req.getWebApp().getMetaClass(getClass()); @@ -4523,6 +4559,8 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve throw new ServletException(); Cookie cookie = new Cookie("iconSize", Functions.validateIconSize(qs)); cookie.setMaxAge(/* ~4 mo. */9999999); // #762 + cookie.setSecure(req.isSecure()); + cookie.setHttpOnly(true); rsp.addCookie(cookie); String ref = req.getHeader("Referer"); if(ref==null) ref="."; @@ -4704,6 +4742,22 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve public List getManagementLinks() { return ManagementLink.all(); } + + // for Jelly + @Restricted(NoExternalUse.class) + public Map> getCategorizedManagementLinks() { + Map> byCategory = new TreeMap<>(); + for (ManagementLink link : ManagementLink.all()) { + if (link.getIconFileName() == null) { + continue; + } + if (!Jenkins.get().hasPermission(link.getRequiredPermission())) { + continue; + } + byCategory.computeIfAbsent(link.getCategory(), c -> new ArrayList<>()).add(link); + } + return byCategory; + } /** * If set, a currently active setup wizard - e.g. installation @@ -5146,6 +5200,12 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } } + @Restricted(NoExternalUse.class) + public boolean shouldShowStackTrace() { + // Used by oops.jelly + return Boolean.getBoolean(Jenkins.class.getName() + ".SHOW_STACK_TRACE"); + } + /** * Hash of {@link #VERSION}. */ @@ -5232,6 +5292,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve */ static final String WORKSPACES_DIR_PROP = Jenkins.class.getName() + ".workspacesDir"; + /** * Automatically try to launch an agent when Jenkins is initialized or a new agent computer is created. */ @@ -5254,7 +5315,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve *

This permission is disabled by default and support for it considered experimental. * Administrators can set the system property {@code jenkins.security.ManagePermission} to enable it.

* - * @since TODO + * @since 2.222 */ @Restricted(Beta.class) public static final Permission MANAGE = new Permission(PERMISSIONS, "Manage", @@ -5263,8 +5324,26 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve SystemProperties.getBoolean("jenkins.security.ManagePermission"), new PermissionScope[]{PermissionScope.JENKINS}); + /** + * Allows read-only access to large parts of the system configuration. + * + * When combined with {@link #MANAGE}, it is expected that everything is shown as if only {@link #SYSTEM_READ} was granted, + * but that only options editable by users with {@link #MANAGE} are editable. + */ + @Restricted(Beta.class) + public static final Permission SYSTEM_READ = new Permission(PERMISSIONS, "SystemRead", + Messages._Jenkins_SystemRead_Description(), + ADMINISTER, + SystemProperties.getBoolean("jenkins.security.SystemReadPermission"), + new PermissionScope[]{PermissionScope.JENKINS}); + + @Restricted(NoExternalUse.class) // called by jelly + public static final Permission[] MANAGE_AND_SYSTEM_READ = + new Permission[] { MANAGE, SYSTEM_READ }; public static final Permission READ = new Permission(PERMISSIONS,"Read",Messages._Hudson_ReadPermission_Description(),Permission.READ,PermissionScope.JENKINS); + /** @deprecated in Jenkins 2.222 use {@link Jenkins#ADMINISTER} instead */ + @Deprecated public static final Permission RUN_SCRIPTS = new Permission(PERMISSIONS, "RunScripts", Messages._Hudson_RunScriptsPermission_Description(),ADMINISTER,PermissionScope.JENKINS); /** diff --git a/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java b/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java index 4f526e9774..21d49c0d25 100644 --- a/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java +++ b/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java @@ -14,7 +14,7 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.QueryParameter; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.Nullable; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.servlet.ServletContext; @@ -26,8 +26,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import static hudson.Util.fixNull; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Stores the location of Jenkins (e-mail address and the HTTP URL.) @@ -64,7 +64,7 @@ public class JenkinsLocationConfiguration extends GlobalConfiguration implements // just to suppress warnings private transient String charset,useSsl; - public static @Nonnull JenkinsLocationConfiguration get() { + public static @NonNull JenkinsLocationConfiguration get() { return GlobalConfiguration.all().getInstance(JenkinsLocationConfiguration.class); } @@ -72,7 +72,7 @@ public class JenkinsLocationConfiguration extends GlobalConfiguration implements * Gets local configuration. For explanation when it could die, see {@link #get()} */ @Restricted(NoExternalUse.class) - public static @Nonnull JenkinsLocationConfiguration getOrDie(){ + public static @NonNull JenkinsLocationConfiguration getOrDie(){ JenkinsLocationConfiguration config = JenkinsLocationConfiguration.get(); if (config == null) { throw new IllegalStateException("JenkinsLocationConfiguration instance is missing. Probably the Jenkins instance is not fully loaded at this time."); @@ -113,7 +113,7 @@ public class JenkinsLocationConfiguration extends GlobalConfiguration implements * Gets the service administrator e-mail address. * @return Admin address or "address not configured" stub */ - public @Nonnull String getAdminAddress() { + public @NonNull String getAdminAddress() { String v = adminAddress; if(v==null) v = Messages.Mailer_Address_Not_Configured(); return v; diff --git a/core/src/main/java/jenkins/model/JobGlobalBuildDiscarderStrategy.java b/core/src/main/java/jenkins/model/JobGlobalBuildDiscarderStrategy.java index bb5523f68b..8260af8c34 100644 --- a/core/src/main/java/jenkins/model/JobGlobalBuildDiscarderStrategy.java +++ b/core/src/main/java/jenkins/model/JobGlobalBuildDiscarderStrategy.java @@ -30,7 +30,7 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.DataBoundConstructor; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; /** @@ -56,7 +56,7 @@ public class JobGlobalBuildDiscarderStrategy extends GlobalBuildDiscarderStrateg @Extension @Symbol("jobBuildDiscarder") public static class DescriptorImpl extends GlobalBuildDiscarderStrategyDescriptor { - @Nonnull + @NonNull @Override public String getDisplayName() { return Messages.JobGlobalBuildDiscarderStrategy_displayName(); diff --git a/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java b/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java index e20e71289c..2c211bdfb8 100644 --- a/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java +++ b/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java @@ -14,6 +14,8 @@ import org.apache.commons.jelly.JellyException; import org.apache.commons.jelly.JellyTagException; import org.apache.commons.jelly.Script; import org.apache.commons.jelly.XMLOutput; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; @@ -128,6 +130,18 @@ public interface ModelObjectWithContextMenu extends ModelObject { return this; } + /** + * Add a header row (no icon, no URL, rendered in header style). + * + * @since TODO + */ + @Restricted(DoNotUse.class) // manage.jelly only + public ContextMenu addHeader(String title) { + final MenuItem item = new MenuItem().withDisplayName(title); + item.header = true; + return add(item); + } + /** * Adds a manually constructed {@link MenuItem} * @@ -259,6 +273,13 @@ public interface ModelObjectWithContextMenu extends ModelObject { */ @Exported public boolean requiresConfirmation; + + /** + * True to display this item as a section header. + * @since TODO + */ + @Exported public boolean header; + /** * If this is a submenu, definition of subitems. */ diff --git a/core/src/main/java/jenkins/model/NodeListener.java b/core/src/main/java/jenkins/model/NodeListener.java index 43d2692292..cc569e18ec 100644 --- a/core/src/main/java/jenkins/model/NodeListener.java +++ b/core/src/main/java/jenkins/model/NodeListener.java @@ -27,7 +27,7 @@ import hudson.ExtensionList; import hudson.ExtensionPoint; import hudson.model.Node; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -45,24 +45,24 @@ public abstract class NodeListener implements ExtensionPoint { /** * Node is being created. */ - protected void onCreated(@Nonnull Node node) {} + protected void onCreated(@NonNull Node node) {} /** * Node is being updated. */ - protected void onUpdated(@Nonnull Node oldOne, @Nonnull Node newOne) {} + protected void onUpdated(@NonNull Node oldOne, @NonNull Node newOne) {} /** * Node is being deleted. */ - protected void onDeleted(@Nonnull Node node) {} + protected void onDeleted(@NonNull Node node) {} /** * Inform listeners that node is being created. * * @param node A node being created. */ - public static void fireOnCreated(@Nonnull Node node) { + public static void fireOnCreated(@NonNull Node node) { for (NodeListener nl: all()) { try { nl.onCreated(node); @@ -78,7 +78,7 @@ public abstract class NodeListener implements ExtensionPoint { * @param oldOne Old configuration. * @param newOne New Configuration. */ - public static void fireOnUpdated(@Nonnull Node oldOne, @Nonnull Node newOne) { + public static void fireOnUpdated(@NonNull Node oldOne, @NonNull Node newOne) { for (NodeListener nl: all()) { try { nl.onUpdated(oldOne, newOne); @@ -93,7 +93,7 @@ public abstract class NodeListener implements ExtensionPoint { * * @param node A node being removed. */ - public static void fireOnDeleted(@Nonnull Node node) { + public static void fireOnDeleted(@NonNull Node node) { for (NodeListener nl: all()) { try { nl.onDeleted(node); @@ -106,7 +106,7 @@ public abstract class NodeListener implements ExtensionPoint { /** * Get all {@link NodeListener}s registered in Jenkins. */ - public static @Nonnull List all() { + public static @NonNull List all() { return ExtensionList.lookup(NodeListener.class); } } diff --git a/core/src/main/java/jenkins/model/Nodes.java b/core/src/main/java/jenkins/model/Nodes.java index 648c867c4b..ed7c73ee46 100644 --- a/core/src/main/java/jenkins/model/Nodes.java +++ b/core/src/main/java/jenkins/model/Nodes.java @@ -38,8 +38,8 @@ import java.util.concurrent.Callable; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.File; import java.io.FileFilter; import java.io.IOException; @@ -67,7 +67,7 @@ public class Nodes implements Saveable { /** * The {@link Jenkins} instance that we are tracking nodes for. */ - @Nonnull + @NonNull private final Jenkins jenkins; /** @@ -82,7 +82,7 @@ public class Nodes implements Saveable { * let this reference escape from a partially constructed {@link Nodes} as when we are passed the * reference the {@link Jenkins} instance has not completed instantiation. */ - /*package*/ Nodes(@Nonnull Jenkins jenkins) { + /*package*/ Nodes(@NonNull Jenkins jenkins) { this.jenkins = jenkins; } @@ -91,7 +91,7 @@ public class Nodes implements Saveable { * * @return the list of nodes. */ - @Nonnull + @NonNull public List getNodes() { return new ArrayList<>(nodes.values()); } @@ -102,7 +102,7 @@ public class Nodes implements Saveable { * @param nodes the new list of nodes. * @throws IOException if the new list of nodes could not be persisted. */ - public void setNodes(final @Nonnull Collection nodes) throws IOException { + public void setNodes(final @NonNull Collection nodes) throws IOException { Queue.withLock(new Runnable() { @Override public void run() { @@ -126,7 +126,7 @@ public class Nodes implements Saveable { * @param node the new node. * @throws IOException if the list of nodes could not be persisted. */ - public void addNode(final @Nonnull Node node) throws IOException { + public void addNode(final @NonNull Node node) throws IOException { Node oldNode = nodes.get(node.getNodeName()); if (node != oldNode) { // TODO we should not need to lock the queue for adding nodes but until we have a way to update the @@ -170,7 +170,7 @@ public class Nodes implements Saveable { * @param node the node to be persisted. * @throws IOException if the node could not be persisted. */ - private void persistNode(final @Nonnull Node node) throws IOException { + private void persistNode(final @NonNull Node node) throws IOException { // no need for a full save() so we just do the minimum if (node instanceof EphemeralNode) { Util.deleteRecursive(new File(getNodesDir(), node.getNodeName())); @@ -192,7 +192,7 @@ public class Nodes implements Saveable { * @throws IOException if the node could not be persisted. * @since 1.634 */ - public boolean updateNode(final @Nonnull Node node) throws IOException { + public boolean updateNode(final @NonNull Node node) throws IOException { boolean exists; try { exists = Queue.withLock(new Callable() { @@ -227,7 +227,7 @@ public class Nodes implements Saveable { * @return {@code true} if node was replaced. * @since 2.8 */ - public boolean replaceNode(final Node oldOne, final @Nonnull Node newOne) throws IOException { + public boolean replaceNode(final Node oldOne, final @NonNull Node newOne) throws IOException { if (oldOne == nodes.get(oldOne.getNodeName())) { // use the queue lock until Nodes has a way of directly modifying a single node. Queue.withLock(new Runnable() { @@ -257,7 +257,7 @@ public class Nodes implements Saveable { * @param node the node instance to remove. * @throws IOException if the list of nodes could not be persisted. */ - public void removeNode(final @Nonnull Node node) throws IOException { + public void removeNode(final @NonNull Node node) throws IOException { if (node == nodes.get(node.getNodeName())) { Queue.withLock(new Runnable() { @Override diff --git a/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java b/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java index 0e814da555..46fcb49ef0 100644 --- a/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java +++ b/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java @@ -54,7 +54,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import javax.servlet.ServletException; import static javax.servlet.http.HttpServletResponse.SC_CREATED; import static javax.servlet.http.HttpServletResponse.SC_CONFLICT; diff --git a/core/src/main/java/jenkins/model/PeepholePermalink.java b/core/src/main/java/jenkins/model/PeepholePermalink.java index 8029524c99..cd2f5c5c67 100644 --- a/core/src/main/java/jenkins/model/PeepholePermalink.java +++ b/core/src/main/java/jenkins/model/PeepholePermalink.java @@ -19,8 +19,8 @@ import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Convenient base implementation for {@link Permalink}s that satisfy @@ -130,7 +130,7 @@ public abstract class PeepholePermalink extends Permalink implements Predicate cacheFor(@Nonnull File buildDir) { + private static @NonNull Map cacheFor(@NonNull File buildDir) { synchronized (caches) { Map cache = caches.get(buildDir); if (cache == null) { @@ -141,7 +141,7 @@ public abstract class PeepholePermalink extends Permalink implements Predicate load(@Nonnull File buildDir) { + private static @NonNull Map load(@NonNull File buildDir) { Map cache = new TreeMap<>(); File storage = storageFor(buildDir); if (storage.isFile()) { @@ -165,14 +165,14 @@ public abstract class PeepholePermalink extends Permalink implements Predicate job, @CheckForNull Run b) { + protected void updateCache(@NonNull Job job, @CheckForNull Run b) { File buildDir = job.getBuildDir(); Map cache = cacheFor(buildDir); synchronized (cache) { @@ -218,7 +218,7 @@ public abstract class PeepholePermalink extends Permalink implements Predicate run, @Nonnull TaskListener listener) { + public void onCompleted(Run run, @NonNull TaskListener listener) { Job j = run.getParent(); for (PeepholePermalink pp : Util.filter(j.getPermalinks(), PeepholePermalink.class)) { if (pp.apply(run)) { diff --git a/core/src/main/java/jenkins/model/RunIdMigrator.java b/core/src/main/java/jenkins/model/RunIdMigrator.java index 0f08bdfa26..5dde1c0e24 100644 --- a/core/src/main/java/jenkins/model/RunIdMigrator.java +++ b/core/src/main/java/jenkins/model/RunIdMigrator.java @@ -54,8 +54,8 @@ import java.util.TreeMap; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.apache.commons.io.Charsets; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.time.FastDateFormat; @@ -89,7 +89,7 @@ public final class RunIdMigrator { */ private static final Set offeredToUnmigrate = Collections.synchronizedSet(new HashSet<>()); - private @Nonnull Map idToNumber = EMPTY; + private @NonNull Map idToNumber = EMPTY; public RunIdMigrator() {} @@ -290,7 +290,7 @@ public final class RunIdMigrator { * @param id a nonnumeric ID which may be a valid {@link Run#getId} * @return the corresponding {@link Run#number}, or 0 if unknown */ - public synchronized int findNumber(@Nonnull String id) { + public synchronized int findNumber(@NonNull String id) { Integer number = idToNumber.get(id); return number != null ? number : 0; } diff --git a/core/src/main/java/jenkins/model/SimpleGlobalBuildDiscarderStrategy.java b/core/src/main/java/jenkins/model/SimpleGlobalBuildDiscarderStrategy.java index 5b4f68ede3..fd2357eb5b 100644 --- a/core/src/main/java/jenkins/model/SimpleGlobalBuildDiscarderStrategy.java +++ b/core/src/main/java/jenkins/model/SimpleGlobalBuildDiscarderStrategy.java @@ -30,7 +30,7 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.DataBoundConstructor; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; /** @@ -65,7 +65,7 @@ public class SimpleGlobalBuildDiscarderStrategy extends GlobalBuildDiscarderStra @Extension @Symbol("simpleBuildDiscarder") public static class DescriptorImpl extends GlobalBuildDiscarderStrategyDescriptor { - @Nonnull + @NonNull @Override public String getDisplayName() { return Messages.SimpleGlobalBuildDiscarderStrategy_displayName(); diff --git a/core/src/main/java/jenkins/model/TransientActionFactory.java b/core/src/main/java/jenkins/model/TransientActionFactory.java index b3d0579abc..4a5361eec7 100644 --- a/core/src/main/java/jenkins/model/TransientActionFactory.java +++ b/core/src/main/java/jenkins/model/TransientActionFactory.java @@ -37,7 +37,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -78,7 +78,7 @@ public abstract class TransientActionFactory implements ExtensionPoint { * @param target an actionable object * @return a possible empty set of actions (typically either using {@link Collections#emptySet} or {@link Collections#singleton}) */ - public abstract @Nonnull Collection createFor(@Nonnull T target); + public abstract @NonNull Collection createFor(@NonNull T target); /** @see no pairs/tuples in Java */ private static class CacheKey { diff --git a/core/src/main/java/jenkins/model/identity/InstanceIdentityProvider.java b/core/src/main/java/jenkins/model/identity/InstanceIdentityProvider.java index c65e27fa90..489d137322 100644 --- a/core/src/main/java/jenkins/model/identity/InstanceIdentityProvider.java +++ b/core/src/main/java/jenkins/model/identity/InstanceIdentityProvider.java @@ -37,8 +37,8 @@ import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * A source of instance identity. @@ -151,7 +151,7 @@ public abstract class InstanceIdentityProvider InstanceIdentityProvider get( - @Nonnull KeyTypes type) { + @NonNull KeyTypes type) { for (InstanceIdentityProvider provider : ExtensionList.lookup(InstanceIdentityProvider.class)) { try { KeyPair keyPair = provider.getKeyPair(); diff --git a/core/src/main/java/jenkins/model/item_category/Categories.java b/core/src/main/java/jenkins/model/item_category/Categories.java index 4ae25096e6..46a01e07e1 100644 --- a/core/src/main/java/jenkins/model/item_category/Categories.java +++ b/core/src/main/java/jenkins/model/item_category/Categories.java @@ -33,8 +33,8 @@ import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import org.kohsuke.stapler.export.Flavor; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import java.io.IOException; import java.io.Serializable; @@ -69,7 +69,7 @@ public class Categories implements HttpResponse, Serializable { } @CheckForNull - public Category getItem(@Nonnull String id) { + public Category getItem(@NonNull String id) { for (Category category : items) { if (category.getId().equals(id)) { return category; diff --git a/core/src/main/java/jenkins/model/item_category/ItemCategory.java b/core/src/main/java/jenkins/model/item_category/ItemCategory.java index 1f80cc82a1..d81b632f2b 100644 --- a/core/src/main/java/jenkins/model/item_category/ItemCategory.java +++ b/core/src/main/java/jenkins/model/item_category/ItemCategory.java @@ -29,7 +29,7 @@ import hudson.ExtensionList; import hudson.ExtensionPoint; import hudson.model.TopLevelItemDescriptor; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -95,7 +95,7 @@ public abstract class ItemCategory implements ExtensionPoint { * * @return A {@link ItemCategory}, if not found, {@link ItemCategory.UncategorizedCategory} is returned */ - @Nonnull + @NonNull public static ItemCategory getCategory(TopLevelItemDescriptor descriptor) { int order = 0; ExtensionList categories = ExtensionList.lookup(ItemCategory.class); diff --git a/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java b/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java index 6ede80cf59..f2bc32b638 100644 --- a/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java +++ b/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java @@ -39,7 +39,7 @@ import java.util.SortedMap; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import jenkins.util.MemoryReductionUtil; import org.kohsuke.accmod.Restricted; @@ -91,7 +91,7 @@ public abstract class AbstractLazyLoadRunMap extends AbstractMap i /** * Used in {@link #all()} to quickly determine if we've already loaded everything. */ - private boolean fullyLoaded; + private volatile boolean fullyLoaded; /** * Currently visible index. diff --git a/core/src/main/java/jenkins/model/lazy/BuildReference.java b/core/src/main/java/jenkins/model/lazy/BuildReference.java index 0572ad549b..97452cac30 100644 --- a/core/src/main/java/jenkins/model/lazy/BuildReference.java +++ b/core/src/main/java/jenkins/model/lazy/BuildReference.java @@ -10,8 +10,8 @@ import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.lazy.LazyBuildMixIn.RunMixIn; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -108,7 +108,7 @@ public final class BuildReference { * @param referent the thing to load * @return a reference, or null to consult the next factory */ - @CheckForNull Holder make(@Nonnull R referent); + @CheckForNull Holder make(@NonNull R referent); } diff --git a/core/src/main/java/jenkins/model/lazy/BuildReferenceMapAdapter.java b/core/src/main/java/jenkins/model/lazy/BuildReferenceMapAdapter.java index c17a9c647f..4b505ab9cc 100644 --- a/core/src/main/java/jenkins/model/lazy/BuildReferenceMapAdapter.java +++ b/core/src/main/java/jenkins/model/lazy/BuildReferenceMapAdapter.java @@ -4,7 +4,7 @@ import groovy.util.MapEntry; import hudson.util.AdaptedIterator; import hudson.util.Iterators; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.Nullable; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; diff --git a/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java b/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java index 43a5c251c3..f1fd7b7ee9 100644 --- a/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java +++ b/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java @@ -41,8 +41,8 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; @@ -62,7 +62,7 @@ public abstract class LazyBuildMixIn & Queue.Task & private static final Logger LOGGER = Logger.getLogger(LazyBuildMixIn.class.getName()); @SuppressWarnings("deprecation") // [JENKINS-15156] builds accessed before onLoad or onCreatedFromScratch called - private @Nonnull RunMap builds = new RunMap<>(); + private @NonNull RunMap builds = new RunMap<>(); /** * Initializes this mixin. @@ -77,7 +77,7 @@ public abstract class LazyBuildMixIn & Queue.Task & * Normally should not be called as such. * Note that the initial value is replaced during {@link #onCreatedFromScratch} or {@link #onLoad}. */ - public final @Nonnull RunMap getRunMap() { + public final @NonNull RunMap getRunMap() { return builds; } diff --git a/core/src/main/java/jenkins/model/queue/AsynchronousExecution.java b/core/src/main/java/jenkins/model/queue/AsynchronousExecution.java index 3da69e2217..d554b2a1c1 100644 --- a/core/src/main/java/jenkins/model/queue/AsynchronousExecution.java +++ b/core/src/main/java/jenkins/model/queue/AsynchronousExecution.java @@ -34,9 +34,9 @@ import hudson.model.Resource; import hudson.model.ResourceActivity; import hudson.model.ResourceController; import hudson.model.ResourceList; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.GuardedBy; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.GuardedBy; import jenkins.model.Jenkins; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -112,7 +112,7 @@ public abstract class AsynchronousExecution extends RuntimeException { * after releasing any problematic locks. */ @Restricted(NoExternalUse.class) - public synchronized final void setExecutorWithoutCompleting(@Nonnull Executor executor) { + public synchronized final void setExecutorWithoutCompleting(@NonNull Executor executor) { assert this.executor == null; this.executor = executor; } diff --git a/core/src/main/java/jenkins/model/queue/ItemDeletion.java b/core/src/main/java/jenkins/model/queue/ItemDeletion.java index b86a93c2f0..e871264fa2 100644 --- a/core/src/main/java/jenkins/model/queue/ItemDeletion.java +++ b/core/src/main/java/jenkins/model/queue/ItemDeletion.java @@ -34,9 +34,9 @@ import java.util.List; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.GuardedBy; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.GuardedBy; /** * A {@link Queue.QueueDecisionHandler} that blocks items being deleted from entering the queue. @@ -57,7 +57,7 @@ public class ItemDeletion extends Queue.QueueDecisionHandler { private final Set registrations = new HashSet<>(); @GuardedBy("lock") - private boolean _contains(@Nonnull Item item) { + private boolean _contains(@NonNull Item item) { if (registrations.isEmpty()) { // no point walking everything if there is nothing in-flight return false; @@ -82,7 +82,7 @@ public class ItemDeletion extends Queue.QueueDecisionHandler { * @param item the item. * @return {@code true} if the {@link Item} or any of its {@link Item#getParent()} are being deleted. */ - public static boolean contains(@Nonnull Item item) { + public static boolean contains(@NonNull Item item) { ItemDeletion instance = instance(); if (instance == null) { return false; @@ -102,7 +102,7 @@ public class ItemDeletion extends Queue.QueueDecisionHandler { * @return {@code true} if and only if the supplied {@link Item} has been {@linkplain #register(Item)}ed for * deletion. */ - public static boolean isRegistered(@Nonnull Item item) { + public static boolean isRegistered(@NonNull Item item) { ItemDeletion instance = instance(); if (instance == null) { return false; @@ -122,7 +122,7 @@ public class ItemDeletion extends Queue.QueueDecisionHandler { * @return {@code true} if and only if the {@link Item} was registered and the caller is now responsible to call * {@link #deregister(Item)}. */ - public static boolean register(@Nonnull Item item) { + public static boolean register(@NonNull Item item) { ItemDeletion instance = instance(); if (instance == null) { return false; @@ -140,7 +140,7 @@ public class ItemDeletion extends Queue.QueueDecisionHandler { * * @param item the {@link Item} that was to be deleted and is now either deleted or the delete was aborted. */ - public static void deregister(@Nonnull Item item) { + public static void deregister(@NonNull Item item) { ItemDeletion instance = instance(); if (instance != null) { instance.lock.writeLock().lock(); diff --git a/core/src/main/java/jenkins/mvn/GlobalMavenConfig.java b/core/src/main/java/jenkins/mvn/GlobalMavenConfig.java index 477aa34afd..032d194b93 100644 --- a/core/src/main/java/jenkins/mvn/GlobalMavenConfig.java +++ b/core/src/main/java/jenkins/mvn/GlobalMavenConfig.java @@ -8,7 +8,7 @@ import jenkins.tools.ToolConfigurationCategory; import org.jenkinsci.Symbol; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; //as close as it gets to the global Maven Project configuration @Extension(ordinal = 50) @Symbol("maven") @@ -17,7 +17,7 @@ public class GlobalMavenConfig extends GlobalConfiguration implements Persisten private GlobalSettingsProvider globalSettingsProvider; @Override - public @Nonnull ToolConfigurationCategory getCategory() { + public @NonNull ToolConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(ToolConfigurationCategory.class); } @@ -39,7 +39,7 @@ public class GlobalMavenConfig extends GlobalConfiguration implements Persisten return settingsProvider != null ? settingsProvider : new DefaultSettingsProvider(); } - public static @Nonnull GlobalMavenConfig get() { + public static @NonNull GlobalMavenConfig get() { return GlobalConfiguration.all().getInstance(GlobalMavenConfig.class); } diff --git a/core/src/main/java/jenkins/plugins/DetachedPluginsUtil.java b/core/src/main/java/jenkins/plugins/DetachedPluginsUtil.java index be2dce8748..ec08e4b3e3 100644 --- a/core/src/main/java/jenkins/plugins/DetachedPluginsUtil.java +++ b/core/src/main/java/jenkins/plugins/DetachedPluginsUtil.java @@ -11,7 +11,7 @@ import jenkins.util.java.JavaUtils; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -79,7 +79,7 @@ public class DetachedPluginsUtil { * * @since 2.0 */ - @Nonnull + @NonNull public static List getImpliedDependencies(String pluginName, String jenkinsVersion) { List out = new ArrayList<>(); for (DetachedPlugin detached : getDetachedPlugins()) { @@ -107,7 +107,7 @@ public class DetachedPluginsUtil { * @return A {@link List} of {@link DetachedPlugin}s. * @see JavaUtils#getCurrentJavaRuntimeVersionNumber() */ - public static @Nonnull + public static @NonNull List getDetachedPlugins() { return DETACHED_LIST.stream() .filter(plugin -> JavaUtils.getCurrentJavaRuntimeVersionNumber().isNewerThanOrEqualTo(plugin.getMinimumJavaVersion())) @@ -121,8 +121,8 @@ public class DetachedPluginsUtil { * @return A {@link List} of {@link DetachedPlugin}s. * @see #getDetachedPlugins() */ - public static @Nonnull - List getDetachedPlugins(@Nonnull VersionNumber since) { + public static @NonNull + List getDetachedPlugins(@NonNull VersionNumber since) { return getDetachedPlugins().stream() .filter(detachedPlugin -> !detachedPlugin.getSplitWhen().isOlderThan(since)) .collect(Collectors.toList()); @@ -135,7 +135,7 @@ public class DetachedPluginsUtil { * @return {@code true} if the plugin is a plugin that was detached from Jenkins at some * point in the past, otherwise {@code false}. */ - public static boolean isDetachedPlugin(@Nonnull String pluginId) { + public static boolean isDetachedPlugin(@NonNull String pluginId) { for (DetachedPlugin detachedPlugin : getDetachedPlugins()) { if (detachedPlugin.getShortName().equals(pluginId)) { return true; @@ -217,7 +217,7 @@ public class DetachedPluginsUtil { return shortName + " " + splitWhen.toString().replace(".*", "") + " " + requiredVersion; } - @Nonnull + @NonNull public JavaSpecificationVersion getMinimumJavaVersion() { return minJavaVersion; } diff --git a/core/src/main/java/jenkins/scm/RunWithSCM.java b/core/src/main/java/jenkins/scm/RunWithSCM.java index 95e86a8cd3..528ecda039 100644 --- a/core/src/main/java/jenkins/scm/RunWithSCM.java +++ b/core/src/main/java/jenkins/scm/RunWithSCM.java @@ -34,8 +34,8 @@ import hudson.scm.SCM; import hudson.util.AdaptedIterator; import org.kohsuke.stapler.export.Exported; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.AbstractSet; import java.util.HashSet; import java.util.Iterator; @@ -57,7 +57,7 @@ public interface RunWithSCM, * * @return A possibly empty list of {@link ChangeLogSet}s. */ - @Nonnull + @NonNull List> getChangeSets(); /** @@ -90,7 +90,7 @@ public interface RunWithSCM, * can be empty but never null. */ @Exported - @Nonnull default Set getCulprits() { + @NonNull default Set getCulprits() { if (shouldCalculateCulprits()) { return calculateCulprits(); } @@ -120,7 +120,7 @@ public interface RunWithSCM, * @return a non-null {@link Set} of {@link User}s associated with this item. */ @SuppressWarnings("unchecked") - @Nonnull + @NonNull default Set calculateCulprits() { Set r = new HashSet<>(); RunT p = ((RunT)this).getPreviousCompletedBuild(); diff --git a/core/src/main/java/jenkins/scm/SCMDecisionHandler.java b/core/src/main/java/jenkins/scm/SCMDecisionHandler.java index cd6ba8fdb4..b21806009c 100644 --- a/core/src/main/java/jenkins/scm/SCMDecisionHandler.java +++ b/core/src/main/java/jenkins/scm/SCMDecisionHandler.java @@ -28,8 +28,8 @@ import hudson.ExtensionPoint; import hudson.model.Item; import java.util.ArrayList; import java.util.List; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Extension point for various decisions about SCM operations for {@link Item} instances. @@ -43,12 +43,12 @@ public abstract class SCMDecisionHandler implements ExtensionPoint { * * @param item The item. */ - public abstract boolean shouldPoll(@Nonnull Item item); + public abstract boolean shouldPoll(@NonNull Item item); /** * All registered {@link SCMDecisionHandler}s */ - @Nonnull + @NonNull public static ExtensionList all() { return ExtensionList.lookup(SCMDecisionHandler.class); } @@ -59,7 +59,7 @@ public abstract class SCMDecisionHandler implements ExtensionPoint { * @return the first veto or {@code null} if there are no vetos */ @CheckForNull - public static SCMDecisionHandler firstShouldPollVeto(@Nonnull Item item) { + public static SCMDecisionHandler firstShouldPollVeto(@NonNull Item item) { for (SCMDecisionHandler handler : all()) { if (!handler.shouldPoll(item)) { return handler; @@ -73,8 +73,8 @@ public abstract class SCMDecisionHandler implements ExtensionPoint { * @param item the item * @return the {@link SCMDecisionHandler} instances vetoing the polling of the specified item. */ - @Nonnull - public static List listShouldPollVetos(@Nonnull Item item) { + @NonNull + public static List listShouldPollVetos(@NonNull Item item) { List result = new ArrayList<>(); for (SCMDecisionHandler handler : all()) { if (!handler.shouldPoll(item)) { diff --git a/core/src/main/java/jenkins/security/ApiTokenProperty.java b/core/src/main/java/jenkins/security/ApiTokenProperty.java index fb1afcfe3a..090648f17d 100644 --- a/core/src/main/java/jenkins/security/ApiTokenProperty.java +++ b/core/src/main/java/jenkins/security/ApiTokenProperty.java @@ -60,9 +60,9 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.Immutable; import org.apache.commons.lang.StringUtils; import org.kohsuke.accmod.Restricted; @@ -153,7 +153,7 @@ public class ApiTokenProperty extends UserProperty { * if the user has no appropriate permissions. * @since 1.426, and since 1.638 the method performs security checks */ - @Nonnull + @NonNull public String getApiToken() { LOGGER.log(Level.FINE, "Deprecated usage of getApiToken"); if(LOGGER.isLoggable(Level.FINER)){ @@ -172,7 +172,7 @@ public class ApiTokenProperty extends UserProperty { return apiToken != null; } - @Nonnull + @NonNull @Restricted(NoExternalUse.class) /*package*/ String getApiTokenInsecure() { if(apiToken == null){ @@ -251,7 +251,7 @@ public class ApiTokenProperty extends UserProperty { public final Date lastUseDate; public final long numDaysUse; - public TokenInfoAndStats(@Nonnull ApiTokenStore.HashedToken token, @Nonnull ApiTokenStats.SingleTokenStats stats) { + public TokenInfoAndStats(@NonNull ApiTokenStore.HashedToken token, @NonNull ApiTokenStats.SingleTokenStats stats) { this.uuid = token.getUuid(); this.name = token.getName(); this.creationDate = token.getCreationDate(); diff --git a/core/src/main/java/jenkins/security/BasicApiTokenHelper.java b/core/src/main/java/jenkins/security/BasicApiTokenHelper.java index 5c73ba5f94..ddbd8536de 100644 --- a/core/src/main/java/jenkins/security/BasicApiTokenHelper.java +++ b/core/src/main/java/jenkins/security/BasicApiTokenHelper.java @@ -30,7 +30,7 @@ import jenkins.security.apitoken.ApiTokenPropertyConfiguration; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; diff --git a/core/src/main/java/jenkins/security/ChannelConfigurator.java b/core/src/main/java/jenkins/security/ChannelConfigurator.java index 87e0c33d0c..4ea8764f3b 100644 --- a/core/src/main/java/jenkins/security/ChannelConfigurator.java +++ b/core/src/main/java/jenkins/security/ChannelConfigurator.java @@ -6,7 +6,7 @@ import hudson.remoting.Channel; import hudson.remoting.ChannelBuilder; import hudson.slaves.SlaveComputer; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.Nullable; /** * Intercepts the new creation of {@link Channel} and tweak its configuration. diff --git a/core/src/main/java/jenkins/security/ClassFilterImpl.java b/core/src/main/java/jenkins/security/ClassFilterImpl.java index 98e2073c01..62643b19ce 100644 --- a/core/src/main/java/jenkins/security/ClassFilterImpl.java +++ b/core/src/main/java/jenkins/security/ClassFilterImpl.java @@ -51,8 +51,8 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; import jenkins.util.SystemProperties; import org.apache.commons.io.IOUtils; @@ -264,7 +264,7 @@ public class ClassFilterImpl extends ClassFilter { * @return something typically like {@code file:/…/plugins/structs/WEB-INF/lib/structs-1.10.jar}; * or null for classes in the Java Platform, some generated classes, etc. */ - private static @CheckForNull String codeSource(@Nonnull Class c) { + private static @CheckForNull String codeSource(@NonNull Class c) { CodeSource cs = c.getProtectionDomain().getCodeSource(); if (cs == null) { return null; diff --git a/core/src/main/java/jenkins/security/ConfidentialKey.java b/core/src/main/java/jenkins/security/ConfidentialKey.java index 8ef998a376..ccd7dc290f 100644 --- a/core/src/main/java/jenkins/security/ConfidentialKey.java +++ b/core/src/main/java/jenkins/security/ConfidentialKey.java @@ -4,7 +4,7 @@ import hudson.scm.SCM; import hudson.tasks.Builder; import hudson.util.Secret; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import java.io.IOException; import jenkins.slaves.JnlpAgentReceiver; diff --git a/core/src/main/java/jenkins/security/ConfidentialStore.java b/core/src/main/java/jenkins/security/ConfidentialStore.java index e9b79e0581..194740cc09 100644 --- a/core/src/main/java/jenkins/security/ConfidentialStore.java +++ b/core/src/main/java/jenkins/security/ConfidentialStore.java @@ -7,13 +7,16 @@ import hudson.util.Secret; import jenkins.model.Jenkins; import org.kohsuke.MetaInfServices; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Iterator; +import java.util.Map; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -51,6 +54,9 @@ public abstract class ConfidentialStore { */ protected abstract @CheckForNull byte[] load(ConfidentialKey key) throws IOException; + // TODO consider promoting to public, and offering a default implementation of randomBytes (via the usual Util.isOverridden binary compat trick) + abstract SecureRandom secureRandom(); + /** * Works like {@link SecureRandom#nextBytes(byte[])}. * @@ -61,10 +67,13 @@ public abstract class ConfidentialStore { /** * Retrieves the currently active singleton instance of {@link ConfidentialStore}. */ - public static @Nonnull ConfidentialStore get() { + public static @NonNull ConfidentialStore get() { if (TEST!=null) return TEST.get(); - Jenkins j = Jenkins.get(); + Jenkins j = Jenkins.getInstanceOrNull(); + if (j == null) { + return Mock.INSTANCE; + } Lookup lookup = j.lookup; ConfidentialStore cs = lookup.get(ConfidentialStore.class); if (cs==null) { @@ -91,10 +100,60 @@ public abstract class ConfidentialStore { return cs; } - /** - * Testing only. Used for testing {@link ConfidentialKey} without {@link Jenkins} + /** + * @deprecated No longer needed. */ + @Deprecated /*package*/ static ThreadLocal TEST = null; + static final class Mock extends ConfidentialStore { + + static final Mock INSTANCE = new Mock(); + + private final SecureRandom rand; + + private final Map data = new ConcurrentHashMap<>(); + + Mock() { + // Use a predictable seed to make tests more reproducible. + try { + rand = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException x) { + throw new AssertionError("https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecureRandom", x); + } + rand.setSeed(new byte[] {1, 2, 3, 4}); + } + + void clear() { + data.clear(); + } + + @Override + protected void store(ConfidentialKey key, byte[] payload) throws IOException { + LOGGER.fine(() -> "storing " + key.getId() + " " + hudson.Util.getDigestOf(hudson.Util.toHexString(payload))); + data.put(key.getId(), payload); + } + + @Override + protected byte[] load(ConfidentialKey key) throws IOException { + byte[] payload = data.get(key.getId()); + LOGGER.fine(() -> "loading " + key.getId() + " " + (payload != null ? hudson.Util.getDigestOf(hudson.Util.toHexString(payload)) : "null")); + return payload; + } + + @Override + SecureRandom secureRandom() { + return rand; + } + + @Override + public byte[] randomBytes(int size) { + byte[] random = new byte[size]; + rand.nextBytes(random); + return random; + } + + } + private static final Logger LOGGER = Logger.getLogger(ConfidentialStore.class.getName()); } diff --git a/core/src/main/java/jenkins/security/CustomClassFilter.java b/core/src/main/java/jenkins/security/CustomClassFilter.java index 9fc4b28a3f..4215e5845e 100644 --- a/core/src/main/java/jenkins/security/CustomClassFilter.java +++ b/core/src/main/java/jenkins/security/CustomClassFilter.java @@ -39,7 +39,7 @@ import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import jenkins.model.Jenkins; import jenkins.util.SystemProperties; import org.apache.commons.io.IOUtils; diff --git a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java index 5a16cefc70..4d18357b32 100644 --- a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java +++ b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java @@ -140,6 +140,11 @@ public class DefaultConfidentialStore extends ConfidentialStore { return new File(rootDir, key.getId()); } + @Override + SecureRandom secureRandom() { + return sr; + } + public byte[] randomBytes(int size) { byte[] random = new byte[size]; sr.nextBytes(random); diff --git a/core/src/main/java/jenkins/security/ExceptionTranslationFilter.java b/core/src/main/java/jenkins/security/ExceptionTranslationFilter.java index 92d7ce9e43..9dd7db7f09 100644 --- a/core/src/main/java/jenkins/security/ExceptionTranslationFilter.java +++ b/core/src/main/java/jenkins/security/ExceptionTranslationFilter.java @@ -1,4 +1,5 @@ /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited + * Copyright (c) 2020 CloudBees, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -122,8 +123,7 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean { catch (AuthenticationException | AccessDeniedException ex) { handleException(request, response, chain, ex); } catch (ServletException ex) { - if (ex.getRootCause() instanceof AuthenticationException - || ex.getRootCause() instanceof AccessDeniedException) { + if (ex.getRootCause() instanceof AuthenticationException || ex.getRootCause() instanceof AccessDeniedException) { handleException(request, response, chain, (AcegiSecurityException) ex.getRootCause()); } else { @@ -235,4 +235,5 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean { public void destroy() { } -} \ No newline at end of file + +} diff --git a/core/src/main/java/jenkins/security/HMACConfidentialKey.java b/core/src/main/java/jenkins/security/HMACConfidentialKey.java index 8ee81a72d4..1ec8a75831 100644 --- a/core/src/main/java/jenkins/security/HMACConfidentialKey.java +++ b/core/src/main/java/jenkins/security/HMACConfidentialKey.java @@ -11,7 +11,6 @@ import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; /** * {@link ConfidentialKey} that's used for creating a token by hashing some information with secret diff --git a/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java b/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java index c8f9880710..1ba8e92ef9 100644 --- a/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java +++ b/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java @@ -15,7 +15,7 @@ import org.acegisecurity.userdetails.UserDetails; import org.jenkinsci.Symbol; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -69,7 +69,7 @@ public class LastGrantedAuthoritiesProperty extends UserProperty { /** * Persist the information with the new {@link UserDetails}. */ - public void update(@Nonnull Authentication auth) throws IOException { + public void update(@NonNull Authentication auth) throws IOException { List roles = new ArrayList<>(); for (GrantedAuthority ga : auth.getAuthorities()) { roles.add(ga.getAuthority()); @@ -99,7 +99,7 @@ public class LastGrantedAuthoritiesProperty extends UserProperty { @Extension public static class SecurityListenerImpl extends SecurityListener { @Override - protected void loggedIn(@Nonnull String username) { + protected void loggedIn(@NonNull String username) { try { // user should have been created but may not have been saved for some realms // but as this is a callback of a successful login we can safely create the user. @@ -116,7 +116,7 @@ public class LastGrantedAuthoritiesProperty extends UserProperty { } @Override - protected void failedToLogIn(@Nonnull String username) { + protected void failedToLogIn(@NonNull String username) { // while this initially seemed like a good idea to avoid allowing wrong impersonation for too long, // doing this means a malicious user can break the impersonation capability // just by failing to login. See ApiTokenFilter that does the following, which seems better: diff --git a/core/src/main/java/jenkins/security/QueueItemAuthenticator.java b/core/src/main/java/jenkins/security/QueueItemAuthenticator.java index 40a243361b..f0deb015bb 100644 --- a/core/src/main/java/jenkins/security/QueueItemAuthenticator.java +++ b/core/src/main/java/jenkins/security/QueueItemAuthenticator.java @@ -12,7 +12,7 @@ import hudson.model.Queue.Task; import hudson.model.queue.Tasks; import java.util.Calendar; import java.util.Collections; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import org.acegisecurity.Authentication; /** diff --git a/core/src/main/java/jenkins/security/QueueItemAuthenticatorConfiguration.java b/core/src/main/java/jenkins/security/QueueItemAuthenticatorConfiguration.java index b814cc61d5..75c4cc8536 100644 --- a/core/src/main/java/jenkins/security/QueueItemAuthenticatorConfiguration.java +++ b/core/src/main/java/jenkins/security/QueueItemAuthenticatorConfiguration.java @@ -10,7 +10,7 @@ import net.sf.json.JSONObject; import org.jenkinsci.Symbol; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.util.List; @@ -31,7 +31,7 @@ public class QueueItemAuthenticatorConfiguration extends GlobalConfiguration imp } @Override - public @Nonnull GlobalConfigurationCategory getCategory() { + public @NonNull GlobalConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class); } @@ -57,14 +57,14 @@ public class QueueItemAuthenticatorConfiguration extends GlobalConfiguration imp } } - public static @Nonnull QueueItemAuthenticatorConfiguration get() { + public static @NonNull QueueItemAuthenticatorConfiguration get() { return GlobalConfiguration.all().getInstance(QueueItemAuthenticatorConfiguration.class); } @Extension(ordinal = 100) public static class ProviderImpl extends QueueItemAuthenticatorProvider { - @Nonnull + @NonNull @Override public List getAuthenticators() { return get().getAuthenticators(); diff --git a/core/src/main/java/jenkins/security/QueueItemAuthenticatorProvider.java b/core/src/main/java/jenkins/security/QueueItemAuthenticatorProvider.java index 98572c825d..4243406344 100644 --- a/core/src/main/java/jenkins/security/QueueItemAuthenticatorProvider.java +++ b/core/src/main/java/jenkins/security/QueueItemAuthenticatorProvider.java @@ -4,7 +4,7 @@ import hudson.Extension; import hudson.ExtensionList; import hudson.ExtensionPoint; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -19,7 +19,7 @@ import java.util.NoSuchElementException; */ public abstract class QueueItemAuthenticatorProvider implements ExtensionPoint { - @Nonnull + @NonNull public abstract List getAuthenticators(); public static Iterable authenticators() { diff --git a/core/src/main/java/jenkins/security/RSAConfidentialKey.java b/core/src/main/java/jenkins/security/RSAConfidentialKey.java index fe30c622de..8c5f64b317 100644 --- a/core/src/main/java/jenkins/security/RSAConfidentialKey.java +++ b/core/src/main/java/jenkins/security/RSAConfidentialKey.java @@ -30,7 +30,6 @@ import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; -import java.security.SecureRandom; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; @@ -79,7 +78,7 @@ public abstract class RSAConfidentialKey extends ConfidentialKey { byte[] payload = load(); if (payload == null) { KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA"); - gen.initialize(2048, new SecureRandom()); // going beyond 2048 requires crypto extension + gen.initialize(2048, cs.secureRandom()); // going beyond 2048 requires crypto extension KeyPair keys = gen.generateKeyPair(); priv = (RSAPrivateKey) keys.getPrivate(); pub = (RSAPublicKey) keys.getPublic(); diff --git a/core/src/main/java/jenkins/security/ResourceDomainConfiguration.java b/core/src/main/java/jenkins/security/ResourceDomainConfiguration.java index 775b8dbf03..a39175b4ed 100644 --- a/core/src/main/java/jenkins/security/ResourceDomainConfiguration.java +++ b/core/src/main/java/jenkins/security/ResourceDomainConfiguration.java @@ -33,7 +33,7 @@ import jenkins.model.Jenkins; import jenkins.model.JenkinsLocationConfiguration; import jenkins.model.identity.InstanceIdentityProvider; import jenkins.util.UrlHelper; -import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.IOUtils; import org.jenkinsci.Symbol; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.Beta; @@ -49,10 +49,12 @@ import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.StandardCharsets; import java.security.interfaces.RSAPublicKey; +import java.util.Base64; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; import static jenkins.security.ResourceDomainFilter.ERROR_RESPONSE; @@ -158,7 +160,7 @@ public final class ResourceDomainConfiguration extends GlobalConfiguration { // URL points to a Jenkins instance RSAPublicKey publicKey = InstanceIdentityProvider.RSA.getPublicKey(); if (publicKey != null) { - String identity = Base64.encodeBase64String(publicKey.getEncoded()); + String identity = Base64.getEncoder().encodeToString(publicKey.getEncoded()); if (identity.equals(identityHeader)) { return FormValidation.ok(Messages.ResourceDomainConfiguration_ThisJenkins()); } @@ -168,8 +170,11 @@ public final class ResourceDomainConfiguration extends GlobalConfiguration { } // response is error String responseMessage = httpURLConnection.getResponseMessage(); - if (responseCode == 404 && responseMessage.equals(ERROR_RESPONSE)) { - return FormValidation.ok(Messages.ResourceDomainConfiguration_ResourceResponse()); + if (responseCode == 404) { + String responseBody = String.join("", IOUtils.readLines(httpURLConnection.getErrorStream(), StandardCharsets.UTF_8)); + if (responseMessage.contains(ERROR_RESPONSE) || responseBody.contains(ERROR_RESPONSE)) { + return FormValidation.ok(Messages.ResourceDomainConfiguration_ResourceResponse()); + } } return FormValidation.error(Messages.ResourceDomainConfiguration_FailedIdentityCheck(responseCode, responseMessage)); } diff --git a/core/src/main/java/jenkins/security/ResourceDomainRecommendation.java b/core/src/main/java/jenkins/security/ResourceDomainRecommendation.java index 0cf942c4f3..30b2c2467d 100644 --- a/core/src/main/java/jenkins/security/ResourceDomainRecommendation.java +++ b/core/src/main/java/jenkins/security/ResourceDomainRecommendation.java @@ -26,7 +26,9 @@ package jenkins.security; import hudson.Extension; import hudson.model.AdministrativeMonitor; import hudson.model.DirectoryBrowserSupport; +import hudson.security.Permission; import hudson.util.HttpResponses; +import jenkins.model.Jenkins; import jenkins.util.SystemProperties; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -63,6 +65,7 @@ public class ResourceDomainRecommendation extends AdministrativeMonitor { @RequirePOST public HttpResponse doAct(@QueryParameter String redirect, @QueryParameter String dismiss) throws IOException { + Jenkins.get().checkPermission(Jenkins.ADMINISTER); if (dismiss != null) { disable(true); return HttpResponses.redirectViaContextPath("manage"); @@ -72,4 +75,9 @@ public class ResourceDomainRecommendation extends AdministrativeMonitor { } return HttpResponses.forwardToPreviousPage(); } + + @Override + public Permission getRequiredPermission() { + return Jenkins.SYSTEM_READ; + } } diff --git a/core/src/main/java/jenkins/security/ResourceDomainRootAction.java b/core/src/main/java/jenkins/security/ResourceDomainRootAction.java index 66aba7107f..700b0273d5 100644 --- a/core/src/main/java/jenkins/security/ResourceDomainRootAction.java +++ b/core/src/main/java/jenkins/security/ResourceDomainRootAction.java @@ -42,9 +42,9 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.*; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.Instant; @@ -143,7 +143,7 @@ public class ResourceDomainRootAction implements UnprotectedRootAction { } } - public String getRedirectUrl(@Nonnull Token token, @Nonnull String restOfPath) { + public String getRedirectUrl(@NonNull Token token, @NonNull String restOfPath) { String resourceRootUrl = getResourceRootUrl(); if (!restOfPath.startsWith("/")) { // Unsure whether this can happen -- just be safe here @@ -165,7 +165,7 @@ public class ResourceDomainRootAction implements UnprotectedRootAction { * @return a token that can be used to redirect users to the {@link ResourceDomainRootAction}. */ @CheckForNull - public Token getToken(@Nonnull DirectoryBrowserSupport dbs, @Nonnull StaplerRequest req) { + public Token getToken(@NonNull DirectoryBrowserSupport dbs, @NonNull StaplerRequest req) { // This is the "restOfPath" of the DirectoryBrowserSupport, i.e. the directory/file/pattern "inside" the DBS. final String dbsFile = req.getOriginalRestOfPath(); @@ -195,7 +195,7 @@ public class ResourceDomainRootAction implements UnprotectedRootAction { private final String authenticationName; private final String browserUrl; - InternalResourceRequest(@Nonnull String browserUrl, @Nonnull String authenticationName) { + InternalResourceRequest(@NonNull String browserUrl, @NonNull String authenticationName) { this.browserUrl = browserUrl; this.authenticationName = authenticationName; } @@ -268,7 +268,7 @@ public class ResourceDomainRootAction implements UnprotectedRootAction { private Instant timestamp; @VisibleForTesting - Token (@Nonnull String path, @Nullable String username, @Nonnull Instant timestamp) { + Token (@NonNull String path, @Nullable String username, @NonNull Instant timestamp) { this.path = path; this.username = Util.fixNull(username); this.timestamp = timestamp; diff --git a/core/src/main/java/jenkins/security/SecurityListener.java b/core/src/main/java/jenkins/security/SecurityListener.java index 5ab4771904..d5256aade7 100644 --- a/core/src/main/java/jenkins/security/SecurityListener.java +++ b/core/src/main/java/jenkins/security/SecurityListener.java @@ -31,7 +31,7 @@ import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.acegisecurity.GrantedAuthority; import org.acegisecurity.userdetails.UserDetails; @@ -49,7 +49,7 @@ public abstract class SecurityListener implements ExtensionPoint { * or any other way plugins can propose. * @param details details of the newly authenticated user, such as name and groups. */ - protected void authenticated(@Nonnull UserDetails details){} + protected void authenticated(@NonNull UserDetails details){} /** * Fired when a user tried to authenticate but failed. @@ -58,7 +58,7 @@ public abstract class SecurityListener implements ExtensionPoint { * @param username the user * @see #authenticated */ - protected void failedToAuthenticate(@Nonnull String username){} + protected void failedToAuthenticate(@NonNull String username){} /** * Fired when a user has logged in. Compared to authenticated, there is a notion of storage / cache. @@ -66,7 +66,7 @@ public abstract class SecurityListener implements ExtensionPoint { * It should be called after the {@link org.acegisecurity.context.SecurityContextHolder#getContext()}'s authentication is set. * @param username the user */ - protected void loggedIn(@Nonnull String username){} + protected void loggedIn(@NonNull String username){} /** * @since 2.161 @@ -75,23 +75,23 @@ public abstract class SecurityListener implements ExtensionPoint { * * @param username the user */ - protected void userCreated(@Nonnull String username) {} + protected void userCreated(@NonNull String username) {} /** * Fired when a user has failed to log in. * Would be called after {@link #failedToAuthenticate}. * @param username the user */ - protected void failedToLogIn(@Nonnull String username){} + protected void failedToLogIn(@NonNull String username){} /** * Fired when a user logs out. * @param username the user */ - protected void loggedOut(@Nonnull String username){} + protected void loggedOut(@NonNull String username){} /** @since 1.569 */ - public static void fireAuthenticated(@Nonnull UserDetails details) { + public static void fireAuthenticated(@NonNull UserDetails details) { if (LOGGER.isLoggable(Level.FINE)) { List groups = new ArrayList<>(); for (GrantedAuthority auth : details.getAuthorities()) { @@ -107,7 +107,7 @@ public abstract class SecurityListener implements ExtensionPoint { } /** @since 2.161 */ - public static void fireUserCreated(@Nonnull String username) { + public static void fireUserCreated(@NonNull String username) { LOGGER.log(Level.FINE, "new user created: {0}", username); for (SecurityListener l : all()) { l.userCreated(username); @@ -115,7 +115,7 @@ public abstract class SecurityListener implements ExtensionPoint { } /** @since 1.569 */ - public static void fireFailedToAuthenticate(@Nonnull String username) { + public static void fireFailedToAuthenticate(@NonNull String username) { LOGGER.log(Level.FINE, "failed to authenticate: {0}", username); for (SecurityListener l : all()) { l.failedToAuthenticate(username); @@ -123,7 +123,7 @@ public abstract class SecurityListener implements ExtensionPoint { } /** @since 1.569 */ - public static void fireLoggedIn(@Nonnull String username) { + public static void fireLoggedIn(@NonNull String username) { LOGGER.log(Level.FINE, "logged in: {0}", username); for (SecurityListener l : all()) { l.loggedIn(username); @@ -131,7 +131,7 @@ public abstract class SecurityListener implements ExtensionPoint { } /** @since 1.569 */ - public static void fireFailedToLogIn(@Nonnull String username) { + public static void fireFailedToLogIn(@NonNull String username) { LOGGER.log(Level.FINE, "failed to log in: {0}", username); for (SecurityListener l : all()) { l.failedToLogIn(username); @@ -139,7 +139,7 @@ public abstract class SecurityListener implements ExtensionPoint { } /** @since 1.569 */ - public static void fireLoggedOut(@Nonnull String username) { + public static void fireLoggedOut(@NonNull String username) { LOGGER.log(Level.FINE, "logged out: {0}", username); for (SecurityListener l : all()) { l.loggedOut(username); diff --git a/core/src/main/java/jenkins/security/SuspiciousRequestFilter.java b/core/src/main/java/jenkins/security/SuspiciousRequestFilter.java new file mode 100644 index 0000000000..63b6f6a790 --- /dev/null +++ b/core/src/main/java/jenkins/security/SuspiciousRequestFilter.java @@ -0,0 +1,47 @@ +package jenkins.security; + +import jenkins.util.SystemProperties; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.logging.Logger; + +@Restricted(NoExternalUse.class) +public class SuspiciousRequestFilter implements Filter { + + /** System property name set to true or false to indicate whether or not semicolons should be allowed in URL paths. */ + public static final String ALLOW_SEMICOLONS_IN_PATH = SuspiciousRequestFilter.class.getName() + ".allowSemicolonsInPath"; + public static boolean allowSemicolonsInPath = SystemProperties.getBoolean(ALLOW_SEMICOLONS_IN_PATH, false); + private static final Logger LOGGER = Logger.getLogger(SuspiciousRequestFilter.class.getName()); + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + if (!allowSemicolonsInPath && httpRequest.getRequestURI().contains(";")) { + LOGGER.warning(() -> "Denying HTTP " + httpRequest.getMethod() + " to " + httpRequest.getRequestURI() + + " as it has an illegal semicolon in the path. This behavior can be overridden by setting the system property " + + ALLOW_SEMICOLONS_IN_PATH + " to true. For more information, see https://jenkins.io/redirect/semicolons-in-urls"); + httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Semicolons are not allowed in the request URI"); + } else { + chain.doFilter(request, response); + } + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void destroy() { + } +} diff --git a/core/src/main/java/jenkins/security/UpdateSiteWarningsConfiguration.java b/core/src/main/java/jenkins/security/UpdateSiteWarningsConfiguration.java index a623702258..dc1aa08691 100644 --- a/core/src/main/java/jenkins/security/UpdateSiteWarningsConfiguration.java +++ b/core/src/main/java/jenkins/security/UpdateSiteWarningsConfiguration.java @@ -36,8 +36,8 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -56,28 +56,28 @@ public class UpdateSiteWarningsConfiguration extends GlobalConfiguration impleme private HashSet ignoredWarnings = new HashSet<>(); @Override - public @Nonnull GlobalConfigurationCategory getCategory() { + public @NonNull GlobalConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class); } - @Nonnull + @NonNull public Set getIgnoredWarnings() { return Collections.unmodifiableSet(ignoredWarnings); } - public boolean isIgnored(@Nonnull UpdateSite.Warning warning) { + public boolean isIgnored(@NonNull UpdateSite.Warning warning) { return ignoredWarnings.contains(warning.id); } @CheckForNull - public PluginWrapper getPlugin(@Nonnull UpdateSite.Warning warning) { + public PluginWrapper getPlugin(@NonNull UpdateSite.Warning warning) { if (warning.type != UpdateSite.Warning.Type.PLUGIN) { return null; } return Jenkins.get().getPluginManager().getPlugin(warning.component); } - @Nonnull + @NonNull public Set getAllWarnings() { HashSet allWarnings = new HashSet<>(); @@ -90,7 +90,7 @@ public class UpdateSiteWarningsConfiguration extends GlobalConfiguration impleme return allWarnings; } - @Nonnull + @NonNull public Set getApplicableWarnings() { Set allWarnings = getAllWarnings(); diff --git a/core/src/main/java/jenkins/security/UpdateSiteWarningsMonitor.java b/core/src/main/java/jenkins/security/UpdateSiteWarningsMonitor.java index c6d8d05a16..3098f94adb 100644 --- a/core/src/main/java/jenkins/security/UpdateSiteWarningsMonitor.java +++ b/core/src/main/java/jenkins/security/UpdateSiteWarningsMonitor.java @@ -29,6 +29,7 @@ import hudson.ExtensionList; import hudson.PluginWrapper; import hudson.model.AdministrativeMonitor; import hudson.model.UpdateSite; +import hudson.security.Permission; import hudson.util.HttpResponses; import jenkins.model.Jenkins; import org.kohsuke.accmod.Restricted; @@ -141,6 +142,7 @@ public class UpdateSiteWarningsMonitor extends AdministrativeMonitor { */ @RequirePOST public HttpResponse doForward(@QueryParameter String fix, @QueryParameter String configure) { + Jenkins.get().checkPermission(Jenkins.ADMINISTER); if (fix != null) { return HttpResponses.redirectViaContextPath("pluginManager"); } @@ -162,6 +164,11 @@ public class UpdateSiteWarningsMonitor extends AdministrativeMonitor { return getActiveWarnings().size() < configuration.getApplicableWarnings().size(); } + @Override + public Permission getRequiredPermission() { + return Jenkins.SYSTEM_READ; + } + @Override public String getDisplayName() { return Messages.UpdateSiteWarningsMonitor_DisplayName(); diff --git a/core/src/main/java/jenkins/security/UserDetailsCache.java b/core/src/main/java/jenkins/security/UserDetailsCache.java index 6470523800..653164efd4 100644 --- a/core/src/main/java/jenkins/security/UserDetailsCache.java +++ b/core/src/main/java/jenkins/security/UserDetailsCache.java @@ -36,8 +36,8 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.springframework.dao.DataAccessException; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -117,7 +117,7 @@ public final class UserDetailsCache { * @throws DataAccessException if user could not be found for a repository-specific reason * @throws ExecutionException if anything else went wrong in the cache lookup/retrieval */ - @Nonnull + @NonNull public UserDetails loadUserByUsername(String idOrFullName) throws UsernameNotFoundException, DataAccessException, ExecutionException { Boolean exists = existenceCache.getIfPresent(idOrFullName); if(exists != null && !exists) { diff --git a/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java b/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java index d634682172..ef053b9943 100644 --- a/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java +++ b/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java @@ -33,8 +33,8 @@ import hudson.model.listeners.SaveableListener; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -108,7 +108,7 @@ public class ApiTokenStats implements Saveable { @Deprecated // to force even if someone wants to remove the one from the class @Restricted(NoExternalUse.class) - void setParent(@Nonnull File parent) { + void setParent(@NonNull File parent) { this.parent = parent; } @@ -119,7 +119,7 @@ public class ApiTokenStats implements Saveable { /** * Will trigger the save if there is some modification */ - public synchronized void removeId(@Nonnull String tokenUuid) { + public synchronized void removeId(@NonNull String tokenUuid) { if(areStatsDisabled()){ return; } @@ -133,7 +133,7 @@ public class ApiTokenStats implements Saveable { /** * Will trigger the save */ - public @Nonnull SingleTokenStats updateUsageForId(@Nonnull String tokenUuid) { + public @NonNull SingleTokenStats updateUsageForId(@NonNull String tokenUuid) { if(areStatsDisabled()){ return new SingleTokenStats(tokenUuid); } @@ -142,7 +142,7 @@ public class ApiTokenStats implements Saveable { } - private synchronized SingleTokenStats updateUsageForIdIfNeeded(@Nonnull String tokenUuid) { + private synchronized SingleTokenStats updateUsageForIdIfNeeded(@NonNull String tokenUuid) { SingleTokenStats stats = findById(tokenUuid) .orElseGet(() -> { SingleTokenStats result = new SingleTokenStats(tokenUuid); @@ -156,7 +156,7 @@ public class ApiTokenStats implements Saveable { return stats; } - public synchronized @Nonnull SingleTokenStats findTokenStatsById(@Nonnull String tokenUuid) { + public synchronized @NonNull SingleTokenStats findTokenStatsById(@NonNull String tokenUuid) { if(areStatsDisabled()){ return new SingleTokenStats(tokenUuid); } @@ -166,7 +166,7 @@ public class ApiTokenStats implements Saveable { .orElse(new SingleTokenStats(tokenUuid)); } - private @Nonnull Optional findById(@Nonnull String tokenUuid) { + private @NonNull Optional findById(@NonNull String tokenUuid) { return tokenStats.stream() .filter(s -> s.tokenUuid.equals(tokenUuid)) .findFirst(); @@ -228,7 +228,7 @@ public class ApiTokenStats implements Saveable { @Deprecated // to force even if someone wants to remove the one from the class @Restricted(NoExternalUse.class) - public static @Nonnull ApiTokenStats load(@CheckForNull File parent) { + public static @NonNull ApiTokenStats load(@CheckForNull File parent) { // even if we are not using statistics, we load the existing one in case the configuration // is enabled afterwards to avoid erasing data @@ -250,7 +250,7 @@ public class ApiTokenStats implements Saveable { *

* If the folder does not exist yet, a fresh new instance is created. */ - public static @Nonnull ApiTokenStats load(@Nonnull User user) { + public static @NonNull ApiTokenStats load(@NonNull User user) { // even if we are not using statistics, we load the existing one in case the configuration // is enabled afterwards to avoid erasing data @@ -271,7 +271,7 @@ public class ApiTokenStats implements Saveable { } @VisibleForTesting - static @CheckForNull ApiTokenStats internalLoad(@Nonnull File userFolder) { + static @CheckForNull ApiTokenStats internalLoad(@NonNull File userFolder) { ApiTokenStats apiTokenStats = null; XmlFile statsFile = getConfigFile(userFolder); if (statsFile.exists()) { @@ -286,7 +286,7 @@ public class ApiTokenStats implements Saveable { return apiTokenStats; } - protected static @Nonnull XmlFile getConfigFile(@Nonnull File parent) { + protected static @NonNull XmlFile getConfigFile(@NonNull File parent) { return new XmlFile(new File(parent, "apiTokenStats.xml")); } diff --git a/core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java b/core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java index 9fae97e9ff..0e996f1324 100644 --- a/core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java +++ b/core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java @@ -32,10 +32,10 @@ import org.apache.commons.lang.StringUtils; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import net.jcip.annotations.Immutable; import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -87,7 +87,7 @@ public class ApiTokenStore { } @SuppressFBWarnings("NP_NONNULL_RETURN_VIOLATION") - public synchronized @Nonnull Collection getTokenListSortedByName() { + public synchronized @NonNull Collection getTokenListSortedByName() { return tokenList.stream() .sorted(SORT_BY_LOWERCASED_NAME) .collect(Collectors.toList()); @@ -101,7 +101,7 @@ public class ApiTokenStore { * Defensive approach to avoid involuntary change since the UUIDs are generated at startup only for UI * and so between restart they change */ - public synchronized void reconfigure(@Nonnull Map tokenStoreDataMap) { + public synchronized void reconfigure(@NonNull Map tokenStoreDataMap) { tokenList.forEach(hashedToken -> { JSONObject receivedTokenData = tokenStoreDataMap.get(hashedToken.uuid); if (receivedTokenData == null) { @@ -122,7 +122,7 @@ public class ApiTokenStore { /** * Remove the legacy token present and generate a new one using the given secret. */ - public synchronized void regenerateTokenFromLegacy(@Nonnull Secret newLegacyApiToken) { + public synchronized void regenerateTokenFromLegacy(@NonNull Secret newLegacyApiToken) { deleteAllLegacyAndGenerateNewOne(newLegacyApiToken, false); } @@ -131,13 +131,13 @@ public class ApiTokenStore { *

* Otherwise, no effect. */ - public synchronized void regenerateTokenFromLegacyIfRequired(@Nonnull Secret newLegacyApiToken) { + public synchronized void regenerateTokenFromLegacyIfRequired(@NonNull Secret newLegacyApiToken) { if(tokenList.stream().noneMatch(HashedToken::isLegacy)){ deleteAllLegacyAndGenerateNewOne(newLegacyApiToken, true); } } - private void deleteAllLegacyAndGenerateNewOne(@Nonnull Secret newLegacyApiToken, boolean migrationFromExistingLegacy) { + private void deleteAllLegacyAndGenerateNewOne(@NonNull Secret newLegacyApiToken, boolean migrationFromExistingLegacy) { deleteAllLegacyTokens(); addLegacyToken(newLegacyApiToken, migrationFromExistingLegacy); } @@ -147,7 +147,7 @@ public class ApiTokenStore { tokenList.removeIf(HashedToken::isLegacy); } - private void addLegacyToken(@Nonnull Secret legacyToken, boolean migrationFromExistingLegacy) { + private void addLegacyToken(@NonNull Secret legacyToken, boolean migrationFromExistingLegacy) { String tokenUserUseNormally = Util.getDigestOf(legacyToken.getPlainText()); String secretValueHashed = this.plainSecretToHashInHex(tokenUserUseNormally); @@ -172,7 +172,7 @@ public class ApiTokenStore { * Create a new token with the given name and return it id and secret value. * Result meant to be sent / displayed and then discarded. */ - public synchronized @Nonnull TokenUuidAndPlainValue generateNewToken(@Nonnull String name) { + public synchronized @NonNull TokenUuidAndPlainValue generateNewToken(@NonNull String name) { // 16x8=128bit worth of randomness, using brute-force you need on average 2^127 tries (~10^37) byte[] random = new byte[16]; RANDOM.nextBytes(random); @@ -190,17 +190,17 @@ public class ApiTokenStore { return new TokenUuidAndPlainValue(token.uuid, tokenTheUserWillUse); } - private @Nonnull String plainSecretToHashInHex(@Nonnull String secretValueInPlainText) { + private @NonNull String plainSecretToHashInHex(@NonNull String secretValueInPlainText) { byte[] hashBytes = plainSecretToHashBytes(secretValueInPlainText); return Util.toHexString(hashBytes); } - private @Nonnull byte[] plainSecretToHashBytes(@Nonnull String secretValueInPlainText) { + private @NonNull byte[] plainSecretToHashBytes(@NonNull String secretValueInPlainText) { // ascii is sufficient for hex-format return hashedBytes(secretValueInPlainText.getBytes(StandardCharsets.US_ASCII)); } - private @Nonnull byte[] hashedBytes(byte[] tokenBytes) { + private @NonNull byte[] hashedBytes(byte[] tokenBytes) { MessageDigest digest; try { digest = MessageDigest.getInstance(HASH_ALGORITHM); @@ -214,7 +214,7 @@ public class ApiTokenStore { * Search in the store if there is a token with the same secret as the one given * @return {@code null} iff there is no matching token */ - public synchronized @CheckForNull HashedToken findMatchingToken(@Nonnull String token) { + public synchronized @CheckForNull HashedToken findMatchingToken(@NonNull String token) { String plainToken; if (isLegacyToken(token)) { plainToken = token; @@ -228,7 +228,7 @@ public class ApiTokenStore { /** * Determine if the given token was generated by the legacy system or the new one */ - private boolean isLegacyToken(@Nonnull String token) { + private boolean isLegacyToken(@NonNull String token) { return token.length() != TOKEN_LENGTH_V2; } @@ -237,7 +237,7 @@ public class ApiTokenStore { * @param token assumed the token is not a legacy one and represent the full token (version + hash) * @return the hash part */ - private @Nonnull String getHashOfToken(@Nonnull String token) { + private @NonNull String getHashOfToken(@NonNull String token) { /* * Structure of the token: * @@ -251,7 +251,7 @@ public class ApiTokenStore { * Search in the store if there is a matching token that has the same secret. * @return {@code null} iff there is no matching token */ - private @CheckForNull HashedToken searchMatch(@Nonnull String plainSecret) { + private @CheckForNull HashedToken searchMatch(@NonNull String plainSecret) { byte[] hashedBytes = plainSecretToHashBytes(plainSecret); for (HashedToken token : tokenList) { if (token.match(hashedBytes)) { @@ -268,7 +268,7 @@ public class ApiTokenStore { * @param tokenUuid The identifier of the token, could be retrieved directly from the {@link HashedToken#getUuid()} * @return the revoked token corresponding to the given {@code tokenUuid} if one was found, otherwise {@code null} */ - public synchronized @CheckForNull HashedToken revokeToken(@Nonnull String tokenUuid) { + public synchronized @CheckForNull HashedToken revokeToken(@NonNull String tokenUuid) { for (Iterator iterator = tokenList.iterator(); iterator.hasNext(); ) { HashedToken token = iterator.next(); if (token.uuid.equals(tokenUuid)) { @@ -285,7 +285,7 @@ public class ApiTokenStore { * Given a token identifier and a name, the system will try to find a corresponding token and rename it * @return {@code true} iff the token was found and the rename was successful */ - public synchronized boolean renameToken(@Nonnull String tokenUuid, @Nonnull String newName) { + public synchronized boolean renameToken(@NonNull String tokenUuid, @NonNull String newName) { for (HashedToken token : tokenList) { if (token.uuid.equals(tokenUuid)) { token.rename(newName); @@ -366,7 +366,7 @@ public class ApiTokenStore { } } - public static @Nonnull HashedToken buildNew(@Nonnull String name, @Nonnull HashValue value) { + public static @NonNull HashedToken buildNew(@NonNull String name, @NonNull HashValue value) { HashedToken result = new HashedToken(); result.name = name; result.creationDate = new Date(); @@ -376,7 +376,7 @@ public class ApiTokenStore { return result; } - public static @Nonnull HashedToken buildNewFromLegacy(@Nonnull HashValue value, boolean migrationFromExistingLegacy) { + public static @NonNull HashedToken buildNewFromLegacy(@NonNull HashValue value, boolean migrationFromExistingLegacy) { HashedToken result = new HashedToken(); result.name = Messages.ApiTokenProperty_LegacyTokenName(); if(migrationFromExistingLegacy){ diff --git a/core/src/main/java/jenkins/security/apitoken/LegacyApiTokenAdministrativeMonitor.java b/core/src/main/java/jenkins/security/apitoken/LegacyApiTokenAdministrativeMonitor.java index dd64894006..3320ccb6ef 100644 --- a/core/src/main/java/jenkins/security/apitoken/LegacyApiTokenAdministrativeMonitor.java +++ b/core/src/main/java/jenkins/security/apitoken/LegacyApiTokenAdministrativeMonitor.java @@ -36,8 +36,8 @@ import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.interceptor.RequirePOST; import org.kohsuke.stapler.json.JsonBody; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.io.IOException; import java.util.Date; import java.util.List; @@ -89,14 +89,14 @@ public class LegacyApiTokenAdministrativeMonitor extends AdministrativeMonitor { // used by Jelly view @Restricted(NoExternalUse.class) - public @Nullable ApiTokenStore.HashedToken getLegacyTokenOf(@Nonnull User user) { + public @Nullable ApiTokenStore.HashedToken getLegacyTokenOf(@NonNull User user) { ApiTokenProperty apiTokenProperty = user.getProperty(ApiTokenProperty.class); return apiTokenProperty.getTokenStore().getLegacyToken(); } // used by Jelly view @Restricted(NoExternalUse.class) - public @Nullable ApiTokenProperty.TokenInfoAndStats getLegacyStatsOf(@Nonnull User user, ApiTokenStore.HashedToken legacyToken) { + public @Nullable ApiTokenProperty.TokenInfoAndStats getLegacyStatsOf(@NonNull User user, ApiTokenStore.HashedToken legacyToken) { ApiTokenProperty apiTokenProperty = user.getProperty(ApiTokenProperty.class); if (legacyToken != null) { ApiTokenStats.SingleTokenStats legacyStats = apiTokenProperty.getTokenStats().findTokenStatsById(legacyToken.getUuid()); @@ -112,7 +112,7 @@ public class LegacyApiTokenAdministrativeMonitor extends AdministrativeMonitor { */ // used by Jelly view @Restricted(NoExternalUse.class) - public boolean hasFreshToken(@Nonnull User user, ApiTokenProperty.TokenInfoAndStats legacyStats) { + public boolean hasFreshToken(@NonNull User user, ApiTokenProperty.TokenInfoAndStats legacyStats) { if (legacyStats == null) { return false; } @@ -136,7 +136,7 @@ public class LegacyApiTokenAdministrativeMonitor extends AdministrativeMonitor { */ // used by Jelly view @Restricted(NoExternalUse.class) - public boolean hasMoreRecentlyUsedToken(@Nonnull User user, ApiTokenProperty.TokenInfoAndStats legacyStats) { + public boolean hasMoreRecentlyUsedToken(@NonNull User user, ApiTokenProperty.TokenInfoAndStats legacyStats) { if (legacyStats == null) { return false; } diff --git a/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java b/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java index d5d94b52d5..0982a15fc4 100644 --- a/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java +++ b/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java @@ -6,7 +6,7 @@ import jenkins.FilePathFilter; import jenkins.ReflectiveFilePathFilter; import jenkins.security.ChannelConfigurator; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.Nullable; import javax.inject.Inject; import java.io.File; diff --git a/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java index 36d7f1881e..2bcebf5358 100644 --- a/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java +++ b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java @@ -16,8 +16,8 @@ import org.kohsuke.stapler.StaplerProxy; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.interceptor.RequirePOST; -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckReturnValue; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -94,7 +94,7 @@ public class AdminWhitelistRule implements StaplerProxy { * @return {@code true} if the file was loaded, {@code false} otherwise */ @CheckReturnValue - private boolean loadMasterKillSwitchFile(@Nonnull File f) { + private boolean loadMasterKillSwitchFile(@NonNull File f) { try { if (!f.exists()) return true; return Boolean.parseBoolean(FileUtils.readFileToString(f, Charset.defaultCharset()).trim()); @@ -104,8 +104,8 @@ public class AdminWhitelistRule implements StaplerProxy { } } - @Nonnull - private File getMasterKillSwitchFile(@Nonnull Jenkins jenkins) { + @NonNull + private File getMasterKillSwitchFile(@NonNull Jenkins jenkins) { return new File(jenkins.getRootDir(),"secrets/slave-to-master-security-kill-switch"); } @@ -212,7 +212,7 @@ public class AdminWhitelistRule implements StaplerProxy { public void setMasterKillSwitch(boolean state) { final Jenkins jenkins = Jenkins.get(); try { - jenkins.checkPermission(Jenkins.RUN_SCRIPTS); + jenkins.checkPermission(Jenkins.ADMINISTER); File f = getMasterKillSwitchFile(jenkins); FileUtils.writeStringToFile(f, Boolean.toString(state), Charset.defaultCharset()); // treat the file as the canonical source of information in case write fails @@ -227,7 +227,7 @@ public class AdminWhitelistRule implements StaplerProxy { */ @Override public Object getTarget() { - Jenkins.get().checkPermission(Jenkins.RUN_SCRIPTS); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); return this; } diff --git a/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java index 194e83c813..76ae2ecd2a 100644 --- a/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java @@ -13,7 +13,7 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collection; import java.util.logging.Level; import java.util.logging.Logger; @@ -46,7 +46,7 @@ public class CallableDirectionChecker extends RoleChecker { } @Override - public void check(RoleSensitive subject, @Nonnull Collection expected) throws SecurityException { + public void check(RoleSensitive subject, @NonNull Collection expected) throws SecurityException { final String name = subject.getClass().getName(); if (expected.contains(Roles.MASTER)) { diff --git a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java index 66e20fdd22..96322cafbb 100644 --- a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java +++ b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java @@ -2,7 +2,7 @@ package jenkins.security.s2m; import hudson.Extension; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.inject.Inject; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; @@ -25,7 +25,7 @@ public class MasterKillSwitchConfiguration extends GlobalConfiguration { Jenkins jenkins; @Override - public @Nonnull GlobalConfigurationCategory getCategory() { + public @NonNull GlobalConfigurationCategory getCategory() { return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class); } @@ -48,7 +48,7 @@ public class MasterKillSwitchConfiguration extends GlobalConfiguration { * Unless this option is relevant, we don't let users choose this. */ public boolean isRelevant() { - return jenkins.hasPermission(Jenkins.RUN_SCRIPTS) && jenkins.isUseSecurity(); + return jenkins.hasPermission(Jenkins.ADMINISTER) && jenkins.isUseSecurity(); } } diff --git a/core/src/main/java/jenkins/security/s2m/RejectedCallable.java b/core/src/main/java/jenkins/security/s2m/RejectedCallable.java index 5c50dbaeb3..a0242c5329 100644 --- a/core/src/main/java/jenkins/security/s2m/RejectedCallable.java +++ b/core/src/main/java/jenkins/security/s2m/RejectedCallable.java @@ -3,7 +3,7 @@ package jenkins.security.s2m; import hudson.PluginWrapper; import jenkins.model.Jenkins; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * @author Kohsuke Kawaguchi diff --git a/core/src/main/java/jenkins/security/seed/UserSeedChangeListener.java b/core/src/main/java/jenkins/security/seed/UserSeedChangeListener.java index d8f9173952..2f777a573b 100644 --- a/core/src/main/java/jenkins/security/seed/UserSeedChangeListener.java +++ b/core/src/main/java/jenkins/security/seed/UserSeedChangeListener.java @@ -30,7 +30,7 @@ import org.apache.tools.ant.ExtensionPoint; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -47,13 +47,13 @@ public abstract class UserSeedChangeListener extends ExtensionPoint { * Called after a seed was changed but before the user is saved. * @param user The target user */ - public abstract void onUserSeedRenewed(@Nonnull User user); + public abstract void onUserSeedRenewed(@NonNull User user); /** * Will notify all the registered listeners about the event * @param user The target user */ - public static void fireUserSeedRenewed(@Nonnull User user) { + public static void fireUserSeedRenewed(@NonNull User user) { for (UserSeedChangeListener l : all()) { try { l.onUserSeedRenewed(user); diff --git a/core/src/main/java/jenkins/security/seed/UserSeedProperty.java b/core/src/main/java/jenkins/security/seed/UserSeedProperty.java index afd3d5e665..0d7919876c 100644 --- a/core/src/main/java/jenkins/security/seed/UserSeedProperty.java +++ b/core/src/main/java/jenkins/security/seed/UserSeedProperty.java @@ -41,7 +41,7 @@ import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.interceptor.RequirePOST; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.security.SecureRandom; import java.util.Objects; @@ -83,7 +83,7 @@ public class UserSeedProperty extends UserProperty { this.renewSeedInternal(); } - public @Nonnull String getSeed() { + public @NonNull String getSeed() { return seed; } @@ -107,7 +107,7 @@ public class UserSeedProperty extends UserProperty { @Extension @Symbol("userSeed") public static final class DescriptorImpl extends UserPropertyDescriptor { - public @Nonnull String getDisplayName() { + public @NonNull String getDisplayName() { return Messages.UserSeedProperty_DisplayName(); } @@ -117,12 +117,12 @@ public class UserSeedProperty extends UserProperty { // only for jelly @Restricted(DoNotUse.class) - public boolean isCurrentUser(@Nonnull User target) { + public boolean isCurrentUser(@NonNull User target) { return Objects.equals(User.current(), target); } @RequirePOST - public synchronized HttpResponse doRenewSessionSeed(@AncestorInPath @Nonnull User u) throws IOException { + public synchronized HttpResponse doRenewSessionSeed(@AncestorInPath @NonNull User u) throws IOException { u.checkPermission(Jenkins.ADMINISTER); if (DISABLE_USER_SEED) { diff --git a/core/src/main/java/jenkins/security/seed/UserSeedSecurityListener.java b/core/src/main/java/jenkins/security/seed/UserSeedSecurityListener.java index f87449341a..754700e62b 100644 --- a/core/src/main/java/jenkins/security/seed/UserSeedSecurityListener.java +++ b/core/src/main/java/jenkins/security/seed/UserSeedSecurityListener.java @@ -32,7 +32,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.http.HttpSession; /** @@ -43,12 +43,12 @@ import javax.servlet.http.HttpSession; @Extension(ordinal = Integer.MAX_VALUE) public class UserSeedSecurityListener extends SecurityListener { @Override - protected void loggedIn(@Nonnull String username) { + protected void loggedIn(@NonNull String username) { putUserSeedInSession(username, true); } @Override - protected void authenticated(@Nonnull UserDetails details) { + protected void authenticated(@NonNull UserDetails details) { putUserSeedInSession(details.getUsername(), false); } diff --git a/core/src/main/java/jenkins/security/stapler/DoActionFilter.java b/core/src/main/java/jenkins/security/stapler/DoActionFilter.java index 9719625826..d65e0fca26 100644 --- a/core/src/main/java/jenkins/security/stapler/DoActionFilter.java +++ b/core/src/main/java/jenkins/security/stapler/DoActionFilter.java @@ -31,7 +31,7 @@ import org.kohsuke.stapler.FunctionList; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.interceptor.InterceptorAnnotation; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.lang.annotation.Annotation; import java.util.logging.Level; import java.util.logging.Logger; @@ -49,7 +49,7 @@ public class DoActionFilter implements FunctionList.Filter { */ private static final Pattern DO_METHOD_REGEX = Pattern.compile("^do[^a-z].*"); - public boolean keep(@Nonnull Function m) { + public boolean keep(@NonNull Function m) { if (m.getAnnotation(StaplerNotDispatchable.class) != null) { return false; diff --git a/core/src/main/java/jenkins/security/stapler/RoutingDecisionProvider.java b/core/src/main/java/jenkins/security/stapler/RoutingDecisionProvider.java index c7cab91839..917b5d32b7 100644 --- a/core/src/main/java/jenkins/security/stapler/RoutingDecisionProvider.java +++ b/core/src/main/java/jenkins/security/stapler/RoutingDecisionProvider.java @@ -25,7 +25,7 @@ package jenkins.security.stapler; import hudson.ExtensionPoint; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; public abstract class RoutingDecisionProvider implements ExtensionPoint { enum Decision { @@ -34,5 +34,5 @@ public abstract class RoutingDecisionProvider implements ExtensionPoint { UNKNOWN } - @Nonnull public abstract Decision decide(@Nonnull String signature); + @NonNull public abstract Decision decide(@NonNull String signature); } diff --git a/core/src/main/java/jenkins/security/stapler/StaplerDispatchValidator.java b/core/src/main/java/jenkins/security/stapler/StaplerDispatchValidator.java index 163258c23b..d67ca6c038 100644 --- a/core/src/main/java/jenkins/security/stapler/StaplerDispatchValidator.java +++ b/core/src/main/java/jenkins/security/stapler/StaplerDispatchValidator.java @@ -36,8 +36,8 @@ import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.WebApp; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletContext; import java.io.IOException; import java.io.InputStream; @@ -92,7 +92,7 @@ public class StaplerDispatchValidator implements DispatchValidator { */ public static /* script-console editable */ boolean DISABLED = SystemProperties.getBoolean(ESCAPE_HATCH); - private static @CheckForNull Boolean setStatus(@Nonnull StaplerRequest req, @CheckForNull Boolean status) { + private static @CheckForNull Boolean setStatus(@NonNull StaplerRequest req, @CheckForNull Boolean status) { if (status == null) { return null; } @@ -101,7 +101,7 @@ public class StaplerDispatchValidator implements DispatchValidator { return status; } - private static @CheckForNull Boolean computeStatusIfNull(@Nonnull StaplerRequest req, @Nonnull Supplier statusIfNull) { + private static @CheckForNull Boolean computeStatusIfNull(@NonNull StaplerRequest req, @NonNull Supplier statusIfNull) { Object requestStatus = req.getAttribute(ATTRIBUTE_NAME); return requestStatus instanceof Boolean ? (Boolean) requestStatus : setStatus(req, statusIfNull.get()); } @@ -114,7 +114,7 @@ public class StaplerDispatchValidator implements DispatchValidator { } @Override - public @CheckForNull Boolean isDispatchAllowed(@Nonnull StaplerRequest req, @Nonnull StaplerResponse rsp) { + public @CheckForNull Boolean isDispatchAllowed(@NonNull StaplerRequest req, @NonNull StaplerResponse rsp) { if (DISABLED) { return true; } @@ -132,7 +132,7 @@ public class StaplerDispatchValidator implements DispatchValidator { } @Override - public @CheckForNull Boolean isDispatchAllowed(@Nonnull StaplerRequest req, @Nonnull StaplerResponse rsp, @Nonnull String viewName, @CheckForNull Object node) { + public @CheckForNull Boolean isDispatchAllowed(@NonNull StaplerRequest req, @NonNull StaplerResponse rsp, @NonNull String viewName, @CheckForNull Object node) { if (DISABLED) { return true; } @@ -150,7 +150,7 @@ public class StaplerDispatchValidator implements DispatchValidator { } @Override - public void allowDispatch(@Nonnull StaplerRequest req, @Nonnull StaplerResponse rsp) { + public void allowDispatch(@NonNull StaplerRequest req, @NonNull StaplerResponse rsp) { if (DISABLED) { return; } @@ -158,7 +158,7 @@ public class StaplerDispatchValidator implements DispatchValidator { } @Override - public void requireDispatchAllowed(@Nonnull StaplerRequest req, @Nonnull StaplerResponse rsp) throws CancelRequestHandlingException { + public void requireDispatchAllowed(@NonNull StaplerRequest req, @NonNull StaplerResponse rsp) throws CancelRequestHandlingException { if (DISABLED) { return; } @@ -170,12 +170,12 @@ public class StaplerDispatchValidator implements DispatchValidator { } @VisibleForTesting - static StaplerDispatchValidator getInstance(@Nonnull ServletContext context) { + static StaplerDispatchValidator getInstance(@NonNull ServletContext context) { return (StaplerDispatchValidator) WebApp.get(context).getDispatchValidator(); } @VisibleForTesting - void loadWhitelist(@Nonnull InputStream in) throws IOException { + void loadWhitelist(@NonNull InputStream in) throws IOException { cache.loadWhitelist(IOUtils.readLines(in)); } @@ -185,7 +185,7 @@ public class StaplerDispatchValidator implements DispatchValidator { // provided as alternative to ConcurrentHashMap.computeIfAbsent which doesn't allow for recursion in the supplier // see https://stackoverflow.com/q/28840047 - private @Nonnull Validator computeIfAbsent(@Nonnull String className, @Nonnull Function constructor) { + private @NonNull Validator computeIfAbsent(@NonNull String className, @NonNull Function constructor) { lock.readLock().lock(); try { if (validators.containsKey(className)) { @@ -209,15 +209,15 @@ public class StaplerDispatchValidator implements DispatchValidator { } } - private @Nonnull Validator find(@Nonnull Class clazz) { + private @NonNull Validator find(@NonNull Class clazz) { return computeIfAbsent(clazz.getName(), name -> create(clazz)); } - private @Nonnull Validator find(@Nonnull String className) { + private @NonNull Validator find(@NonNull String className) { return computeIfAbsent(className, this::create); } - private @Nonnull Collection findParents(@Nonnull Class clazz) { + private @NonNull Collection findParents(@NonNull Class clazz) { List parents = new ArrayList<>(); Class superclass = clazz.getSuperclass(); if (superclass != null) { @@ -229,7 +229,7 @@ public class StaplerDispatchValidator implements DispatchValidator { return parents; } - private @Nonnull Validator create(@Nonnull Class clazz) { + private @NonNull Validator create(@NonNull Class clazz) { Set allowed = new HashSet<>(); StaplerViews views = clazz.getDeclaredAnnotation(StaplerViews.class); if (views != null) { @@ -243,7 +243,7 @@ public class StaplerDispatchValidator implements DispatchValidator { return new Validator(() -> findParents(clazz), allowed, denied); } - private @Nonnull Validator create(@Nonnull String className) { + private @NonNull Validator create(@NonNull String className) { ClassLoader loader = Jenkins.get().pluginManager.uberClassLoader; return new Validator(() -> { try { @@ -274,7 +274,7 @@ public class StaplerDispatchValidator implements DispatchValidator { } } - private void loadWhitelist(@Nonnull List whitelistLines) { + private void loadWhitelist(@NonNull List whitelistLines) { for (String line : whitelistLines) { if (line.matches("#.*|\\s*")) { // commented line @@ -305,17 +305,17 @@ public class StaplerDispatchValidator implements DispatchValidator { private final Set allowed = ConcurrentHashMap.newKeySet(); private final Set denied = ConcurrentHashMap.newKeySet(); - private Validator(@Nonnull Supplier> parentsSupplier) { + private Validator(@NonNull Supplier> parentsSupplier) { this.parentsSupplier = parentsSupplier; } - private Validator(@Nonnull Supplier> parentsSupplier, @Nonnull Collection allowed, @Nonnull Collection denied) { + private Validator(@NonNull Supplier> parentsSupplier, @NonNull Collection allowed, @NonNull Collection denied) { this(parentsSupplier); this.allowed.addAll(allowed); this.denied.addAll(denied); } - private @Nonnull Collection getParents() { + private @NonNull Collection getParents() { if (parents == null) { synchronized (this) { if (parents == null) { @@ -326,7 +326,7 @@ public class StaplerDispatchValidator implements DispatchValidator { return parents; } - private @CheckForNull Boolean isViewValid(@Nonnull String viewName) { + private @CheckForNull Boolean isViewValid(@NonNull String viewName) { if (allowed.contains(viewName)) { return Boolean.TRUE; } @@ -342,11 +342,11 @@ public class StaplerDispatchValidator implements DispatchValidator { return null; } - private void allowView(@Nonnull String viewName) { + private void allowView(@NonNull String viewName) { allowed.add(viewName); } - private void denyView(@Nonnull String viewName) { + private void denyView(@NonNull String viewName) { denied.add(viewName); } } diff --git a/core/src/main/java/jenkins/security/stapler/StaticRoutingDecisionProvider.java b/core/src/main/java/jenkins/security/stapler/StaticRoutingDecisionProvider.java index cbb5876015..2b63657e80 100644 --- a/core/src/main/java/jenkins/security/stapler/StaticRoutingDecisionProvider.java +++ b/core/src/main/java/jenkins/security/stapler/StaticRoutingDecisionProvider.java @@ -38,7 +38,7 @@ import org.kohsuke.stapler.Function; import org.kohsuke.stapler.WebApp; import org.kohsuke.stapler.lang.FieldRef; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -80,8 +80,8 @@ public class StaticRoutingDecisionProvider extends RoutingDecisionProvider imple * @see Function#getSignature() * @see FieldRef#getSignature() */ - @Nonnull - public synchronized Decision decide(@Nonnull String signature) { + @NonNull + public synchronized Decision decide(@NonNull String signature) { if (whitelistSignaturesFromFixedList == null || whitelistSignaturesFromUserControlledList == null || blacklistSignaturesFromFixedList == null || blacklistSignaturesFromUserControlledList == null) { reload(); @@ -140,7 +140,7 @@ public class StaticRoutingDecisionProvider extends RoutingDecisionProvider imple LOGGER.log(Level.FINE, "Found {0} getter in the standard whitelist", whitelistSignaturesFromFixedList.size()); } - public synchronized StaticRoutingDecisionProvider add(@Nonnull String signature) { + public synchronized StaticRoutingDecisionProvider add(@NonNull String signature) { if (this.whitelistSignaturesFromUserControlledList.add(signature)) { LOGGER.log(Level.INFO, "Signature [{0}] added to the whitelist", signature); save(); @@ -151,7 +151,7 @@ public class StaticRoutingDecisionProvider extends RoutingDecisionProvider imple return this; } - public synchronized StaticRoutingDecisionProvider addBlacklistSignature(@Nonnull String signature) { + public synchronized StaticRoutingDecisionProvider addBlacklistSignature(@NonNull String signature) { if (this.blacklistSignaturesFromUserControlledList.add(signature)) { LOGGER.log(Level.INFO, "Signature [{0}] added to the blacklist", signature); save(); @@ -162,7 +162,7 @@ public class StaticRoutingDecisionProvider extends RoutingDecisionProvider imple return this; } - public synchronized StaticRoutingDecisionProvider remove(@Nonnull String signature) { + public synchronized StaticRoutingDecisionProvider remove(@NonNull String signature) { if (this.whitelistSignaturesFromUserControlledList.remove(signature)) { LOGGER.log(Level.INFO, "Signature [{0}] removed from the whitelist", signature); save(); @@ -173,7 +173,7 @@ public class StaticRoutingDecisionProvider extends RoutingDecisionProvider imple return this; } - public synchronized StaticRoutingDecisionProvider removeBlacklistSignature(@Nonnull String signature) { + public synchronized StaticRoutingDecisionProvider removeBlacklistSignature(@NonNull String signature) { if (this.blacklistSignaturesFromUserControlledList.remove(signature)) { LOGGER.log(Level.INFO, "Signature [{0}] removed from the blacklist", signature); save(); diff --git a/core/src/main/java/jenkins/security/stapler/TypedFilter.java b/core/src/main/java/jenkins/security/stapler/TypedFilter.java index 6f9bdb4019..e95ba84f8b 100644 --- a/core/src/main/java/jenkins/security/stapler/TypedFilter.java +++ b/core/src/main/java/jenkins/security/stapler/TypedFilter.java @@ -13,7 +13,7 @@ import org.kohsuke.stapler.WebApp; import org.kohsuke.stapler.interceptor.InterceptorAnnotation; import org.kohsuke.stapler.lang.FieldRef; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.HashMap; @@ -49,7 +49,7 @@ public class TypedFilter implements FieldRef.Filter, FunctionList.Filter { return SKIP_TYPE_CHECK || isStaplerRelevantCached(clazz); } - private static boolean isStaplerRelevantCached(@Nonnull Class clazz) { + private static boolean isStaplerRelevantCached(@NonNull Class clazz) { if (staplerCache.containsKey(clazz)) { return staplerCache.get(clazz); } @@ -60,11 +60,11 @@ public class TypedFilter implements FieldRef.Filter, FunctionList.Filter { } @Restricted(NoExternalUse.class) - public static boolean isStaplerRelevant(@Nonnull Class clazz) { + public static boolean isStaplerRelevant(@NonNull Class clazz) { return isSpecificClassStaplerRelevant(clazz) || isSuperTypesStaplerRelevant(clazz); } - private static boolean isSuperTypesStaplerRelevant(@Nonnull Class clazz) { + private static boolean isSuperTypesStaplerRelevant(@NonNull Class clazz) { Class superclass = clazz.getSuperclass(); if (superclass != null && isStaplerRelevantCached(superclass)) { return true; @@ -77,7 +77,7 @@ public class TypedFilter implements FieldRef.Filter, FunctionList.Filter { return false; } - private static boolean isSpecificClassStaplerRelevant(@Nonnull Class clazz) { + private static boolean isSpecificClassStaplerRelevant(@NonNull Class clazz) { if (clazz.isAnnotationPresent(StaplerAccessibleType.class)) { return true; } @@ -102,7 +102,7 @@ public class TypedFilter implements FieldRef.Filter, FunctionList.Filter { return false; } - private static boolean isRoutableMethod(@Nonnull Method m) { + private static boolean isRoutableMethod(@NonNull Method m) { for (Annotation a : m.getDeclaredAnnotations()) { if (WebMethodConstants.WEB_METHOD_ANNOTATION_NAMES.contains(a.annotationType().getName())) { return true; @@ -131,7 +131,7 @@ public class TypedFilter implements FieldRef.Filter, FunctionList.Filter { } @Override - public boolean keep(@Nonnull FieldRef fieldRef) { + public boolean keep(@NonNull FieldRef fieldRef) { if (fieldRef.getAnnotation(StaplerNotDispatchable.class) != null) { // explicitly marked as an invalid field @@ -188,7 +188,7 @@ public class TypedFilter implements FieldRef.Filter, FunctionList.Filter { } @Override - public boolean keep(@Nonnull Function function) { + public boolean keep(@NonNull Function function) { if (function.getAnnotation(StaplerNotDispatchable.class) != null) { // explicitly marked as an invalid getter diff --git a/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java b/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java index 1d052cff3d..5d871bd208 100644 --- a/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java +++ b/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java @@ -15,8 +15,8 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; import jenkins.security.ChannelConfigurator; import jenkins.util.SystemProperties; @@ -83,7 +83,7 @@ public class DefaultJnlpSlaveReceiver extends JnlpAgentReceiver { } @Override - public void afterProperties(@Nonnull JnlpConnectionState event) { + public void afterProperties(@NonNull JnlpConnectionState event) { String clientName = event.getProperty(JnlpConnectionState.CLIENT_NAME_KEY); SlaveComputer computer = (SlaveComputer) Jenkins.get().getComputer(clientName); if (computer == null) { @@ -145,7 +145,7 @@ public class DefaultJnlpSlaveReceiver extends JnlpAgentReceiver { } @Override - public void beforeChannel(@Nonnull JnlpConnectionState event) { + public void beforeChannel(@NonNull JnlpConnectionState event) { DefaultJnlpSlaveReceiver.State state = event.getStash(DefaultJnlpSlaveReceiver.State.class); final SlaveComputer computer = state.getNode(); final OutputStream log = computer.openLogFile(); @@ -163,7 +163,7 @@ public class DefaultJnlpSlaveReceiver extends JnlpAgentReceiver { } @Override - public void afterChannel(@Nonnull JnlpConnectionState event) { + public void afterChannel(@NonNull JnlpConnectionState event) { DefaultJnlpSlaveReceiver.State state = event.getStash(DefaultJnlpSlaveReceiver.State.class); final SlaveComputer computer = state.getNode(); try { @@ -180,7 +180,7 @@ public class DefaultJnlpSlaveReceiver extends JnlpAgentReceiver { } @Override - public void channelClosed(@Nonnull JnlpConnectionState event) { + public void channelClosed(@NonNull JnlpConnectionState event) { final String nodeName = event.getProperty(JnlpConnectionState.CLIENT_NAME_KEY); IOException cause = event.getCloseCause(); if (cause instanceof ClosedChannelException) { @@ -192,16 +192,16 @@ public class DefaultJnlpSlaveReceiver extends JnlpAgentReceiver { } private static class State implements JnlpConnectionState.ListenerState { - @Nonnull + @NonNull private final SlaveComputer node; @CheckForNull private OutputStream log; - public State(@Nonnull SlaveComputer node) { + public State(@NonNull SlaveComputer node) { this.node = node; } - @Nonnull + @NonNull public SlaveComputer getNode() { return node; } @@ -211,7 +211,7 @@ public class DefaultJnlpSlaveReceiver extends JnlpAgentReceiver { return log; } - public void setLog(@Nonnull OutputStream log) { + public void setLog(@NonNull OutputStream log) { this.log = log; } } diff --git a/core/src/main/java/jenkins/slaves/JnlpAgentReceiver.java b/core/src/main/java/jenkins/slaves/JnlpAgentReceiver.java index b738b3316a..3bc25d152d 100644 --- a/core/src/main/java/jenkins/slaves/JnlpAgentReceiver.java +++ b/core/src/main/java/jenkins/slaves/JnlpAgentReceiver.java @@ -5,7 +5,7 @@ import hudson.ExtensionPoint; import hudson.Util; import hudson.model.Slave; import java.security.SecureRandom; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.agents.WebSocketAgents; import jenkins.security.HMACConfidentialKey; @@ -70,7 +70,7 @@ public abstract class JnlpAgentReceiver extends JnlpConnectionStateListener impl } @Override - public String getSecretOf(@Nonnull String clientName) { + public String getSecretOf(@NonNull String clientName) { return SLAVE_SECRET.mac(clientName); } } diff --git a/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java b/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java index d1e0c06e4b..081747c6ce 100644 --- a/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java +++ b/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java @@ -25,7 +25,7 @@ package jenkins.slaves; import hudson.util.VersionNumber; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.io.InputStream; import java.util.Properties; @@ -42,10 +42,10 @@ public class RemotingVersionInfo { private static final Logger LOGGER = Logger.getLogger(RemotingVersionInfo.class.getName()); private static final String RESOURCE_NAME="remoting-info.properties"; - @Nonnull + @NonNull private static VersionNumber EMBEDDED_VERSION; - @Nonnull + @NonNull private static VersionNumber MINIMUM_SUPPORTED_VERSION; private RemotingVersionInfo() {} @@ -64,8 +64,8 @@ public class RemotingVersionInfo { MINIMUM_SUPPORTED_VERSION = extractVersion(props, "remoting.minimum.supported.version"); } - @Nonnull - private static VersionNumber extractVersion(@Nonnull Properties props, @Nonnull String propertyName) + @NonNull + private static VersionNumber extractVersion(@NonNull Properties props, @NonNull String propertyName) throws ExceptionInInitializerError { String prop = props.getProperty(propertyName); if (prop == null) { @@ -93,7 +93,7 @@ public class RemotingVersionInfo { * Note that this version may differ from one which is being really used in Jenkins. * @return Remoting version */ - @Nonnull + @NonNull public static VersionNumber getEmbeddedVersion() { return EMBEDDED_VERSION; } @@ -106,7 +106,7 @@ public class RemotingVersionInfo { * This method defines a minimum expected version, so that all calls should use a compatible API. * @return Minimal Remoting version for API calls. */ - @Nonnull + @NonNull public static VersionNumber getMinimumSupportedVersion() { return MINIMUM_SUPPORTED_VERSION; } diff --git a/core/src/main/java/jenkins/slaves/RemotingWorkDirSettings.java b/core/src/main/java/jenkins/slaves/RemotingWorkDirSettings.java index f49a34564a..b5bb967146 100644 --- a/core/src/main/java/jenkins/slaves/RemotingWorkDirSettings.java +++ b/core/src/main/java/jenkins/slaves/RemotingWorkDirSettings.java @@ -32,8 +32,8 @@ import hudson.slaves.SlaveComputer; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.Jenkins; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -58,7 +58,7 @@ public class RemotingWorkDirSettings implements Describable toCommandLineArgs(@Nonnull SlaveComputer computer) { + public List toCommandLineArgs(@NonNull SlaveComputer computer) { if(disabled) { return Collections.emptyList(); } @@ -170,9 +170,9 @@ public class RemotingWorkDirSettings implements Describable run, @Nonnull FilePath workspace, @Nonnull Launcher launcher, - @Nonnull TaskListener listener) throws InterruptedException, IOException; + void perform(@NonNull Run run, @NonNull FilePath workspace, @NonNull Launcher launcher, + @NonNull TaskListener listener) throws InterruptedException, IOException; /** * Marker for explicitly added build actions (as {@link Run#addAction}) which should imply a transient project @@ -112,9 +112,9 @@ public interface SimpleBuildStep extends BuildStep { return Job.class; } - @Nonnull + @NonNull @Override - public Collection createFor(@Nonnull Job j) { + public Collection createFor(@NonNull Job j) { List actions = new LinkedList<>(); Run r = j.getLastSuccessfulBuild(); if (r != null) { diff --git a/core/src/main/java/jenkins/tasks/SimpleBuildWrapper.java b/core/src/main/java/jenkins/tasks/SimpleBuildWrapper.java index 8fcbd3aaff..ceab8e2996 100644 --- a/core/src/main/java/jenkins/tasks/SimpleBuildWrapper.java +++ b/core/src/main/java/jenkins/tasks/SimpleBuildWrapper.java @@ -45,8 +45,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * A generalization of {@link BuildWrapper} that, like {@link SimpleBuildStep}, may be called at various points within a build. @@ -83,7 +83,7 @@ public abstract class SimpleBuildWrapper extends BuildWrapper { public static final class Context { private Disposer disposer; private final Map env = new HashMap<>(); - public @Nonnull Map getEnv() { + public @NonNull Map getEnv() { return env; } /** @@ -104,7 +104,7 @@ public abstract class SimpleBuildWrapper extends BuildWrapper { * Specify an action to take when the block ends. * If not specified, nothing special happens. */ - public void setDisposer(@Nonnull Disposer disposer) { + public void setDisposer(@NonNull Disposer disposer) { if (this.disposer != null) { throw new IllegalStateException("just one disposer"); } @@ -184,7 +184,7 @@ public abstract class SimpleBuildWrapper extends BuildWrapper { * @return a filter which ignores its {@code build} parameter and is {@link Serializable}; or null (the default) * @since 1.608 */ - public @CheckForNull ConsoleLogFilter createLoggerDecorator(@Nonnull Run build) { + public @CheckForNull ConsoleLogFilter createLoggerDecorator(@NonNull Run build) { return null; } diff --git a/core/src/main/java/jenkins/telemetry/Telemetry.java b/core/src/main/java/jenkins/telemetry/Telemetry.java index 6e4538aa47..6525e0c89a 100644 --- a/core/src/main/java/jenkins/telemetry/Telemetry.java +++ b/core/src/main/java/jenkins/telemetry/Telemetry.java @@ -38,8 +38,8 @@ import org.apache.commons.codec.digest.DigestUtils; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -81,7 +81,7 @@ public abstract class Telemetry implements ExtensionPoint { * * @return ID of the collector, never null or empty */ - @Nonnull + @NonNull public String getId() { return getClass().getName(); } @@ -91,7 +91,7 @@ public abstract class Telemetry implements ExtensionPoint { * * @return display name, never null or empty */ - @Nonnull + @NonNull public abstract String getDisplayName(); /** @@ -101,7 +101,7 @@ public abstract class Telemetry implements ExtensionPoint { * * @return collection start date */ - @Nonnull + @NonNull public abstract LocalDate getStart(); /** @@ -111,7 +111,7 @@ public abstract class Telemetry implements ExtensionPoint { * * @return collection end date */ - @Nonnull + @NonNull public abstract LocalDate getEnd(); /** @@ -145,7 +145,7 @@ public abstract class Telemetry implements ExtensionPoint { * Returns true iff we're in the time period during which this is supposed to collect data. * @return true iff we're in the time period during which this is supposed to collect data * - * @since TODO + * @since 2.202 */ public boolean isActivePeriod() { LocalDate now = LocalDate.now(); diff --git a/core/src/main/java/jenkins/telemetry/impl/AutoRefresh.java b/core/src/main/java/jenkins/telemetry/impl/AutoRefresh.java deleted file mode 100644 index cbbe31e3d8..0000000000 --- a/core/src/main/java/jenkins/telemetry/impl/AutoRefresh.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2019, Daniel Beck - * - * 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 jenkins.telemetry.impl; - -import hudson.Extension; -import jenkins.telemetry.Telemetry; -import net.sf.json.JSONObject; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; - -import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import java.time.LocalDate; -import java.util.HashSet; -import java.util.Set; - -@Extension -@Restricted(NoExternalUse.class) -public class AutoRefresh extends Telemetry { - - private static final Set sessionsWithAutoRefresh = new HashSet<>(); - private static final Set sessionsWithoutAutoRefresh = new HashSet<>(); - - @Nonnull - @Override - public String getDisplayName() { - return Messages.AutoRefresh_DisplayName(); - } - - @Nonnull - @Override - public LocalDate getStart() { - return LocalDate.of(2019, 10, 20); - } - - @Nonnull - @Override - public LocalDate getEnd() { - return LocalDate.of(2019, 12, 31); - } - - @Override - public JSONObject createContent() { - int sessionsWithAutoRefreshCount, sessionsWithoutAutoRefreshCount; - synchronized (sessionsWithAutoRefresh) { - sessionsWithAutoRefreshCount = sessionsWithAutoRefresh.size(); - sessionsWithAutoRefresh.clear(); - } - synchronized (sessionsWithoutAutoRefresh) { - sessionsWithoutAutoRefreshCount = sessionsWithoutAutoRefresh.size(); - sessionsWithoutAutoRefresh.clear(); - } - - if (sessionsWithAutoRefreshCount == 0 && sessionsWithoutAutoRefreshCount == 0) { - return null; - } - - JSONObject payload = new JSONObject(); - payload.put("enabled", sessionsWithAutoRefreshCount); - payload.put("disabled", sessionsWithoutAutoRefreshCount); - return payload; - } - - public void recordRequest(HttpServletRequest request, boolean enabled) { - if (Telemetry.isDisabled()) { - return; - } - if (!isActivePeriod()) { - return; - } - HttpSession session = request.getSession(false); - if (session != null) { - String sessionId = session.getId(); - if (enabled) { - synchronized (sessionsWithAutoRefresh) { - sessionsWithAutoRefresh.add(sessionId); - } - } else { - synchronized (sessionsWithoutAutoRefresh) { - sessionsWithoutAutoRefresh.add(sessionId); - } - } - } - } -} diff --git a/core/src/main/java/jenkins/telemetry/impl/SecuritySystemProperties.java b/core/src/main/java/jenkins/telemetry/impl/SecuritySystemProperties.java index d62597fd6f..1d63edb5db 100644 --- a/core/src/main/java/jenkins/telemetry/impl/SecuritySystemProperties.java +++ b/core/src/main/java/jenkins/telemetry/impl/SecuritySystemProperties.java @@ -24,6 +24,7 @@ package jenkins.telemetry.impl; import hudson.Extension; +import hudson.util.VersionNumber; import jenkins.model.Jenkins; import jenkins.telemetry.Telemetry; import jenkins.util.SystemProperties; @@ -31,7 +32,7 @@ import net.sf.json.JSONObject; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.time.LocalDate; @@ -46,31 +47,31 @@ import java.util.TreeMap; @Extension @Restricted(NoExternalUse.class) public class SecuritySystemProperties extends Telemetry { - @Nonnull + @NonNull @Override public String getId() { return "security-system-properties"; } - @Nonnull + @NonNull @Override public LocalDate getStart() { return LocalDate.of(2018, 9, 1); } - @Nonnull + @NonNull @Override public LocalDate getEnd() { return LocalDate.of(2018, 12, 1); } - @Nonnull + @NonNull @Override public String getDisplayName() { return "Use of Security-related Java system properties"; } - @Nonnull + @NonNull @Override public JSONObject createContent() { Map security = new TreeMap<>(); @@ -97,7 +98,8 @@ public class SecuritySystemProperties extends Telemetry { putStringInfo(security, "hudson.security.HudsonPrivateSecurityRealm.ID_REGEX"); Map info = new TreeMap<>(); - info.put("core", Jenkins.getVersion().toString()); + VersionNumber jenkinsVersion = Jenkins.getVersion(); + info.put("core", jenkinsVersion != null ? jenkinsVersion.toString() : "UNKNOWN"); info.put("clientDate", clientDateString()); info.put("properties", security); diff --git a/core/src/main/java/jenkins/telemetry/impl/StaplerDispatches.java b/core/src/main/java/jenkins/telemetry/impl/StaplerDispatches.java index dd03ce2d4b..13115d5c3f 100644 --- a/core/src/main/java/jenkins/telemetry/impl/StaplerDispatches.java +++ b/core/src/main/java/jenkins/telemetry/impl/StaplerDispatches.java @@ -35,7 +35,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.EvaluationTrace; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.time.LocalDate; import java.util.Map; import java.util.Set; @@ -49,19 +49,19 @@ import java.util.concurrent.ConcurrentSkipListSet; @Extension @Restricted(NoExternalUse.class) public class StaplerDispatches extends Telemetry { - @Nonnull + @NonNull @Override public LocalDate getStart() { return LocalDate.of(2018, 10, 10); } - @Nonnull + @NonNull @Override public LocalDate getEnd() { return LocalDate.of(2019, 8, 1); } - @Nonnull + @NonNull @Override public String getDisplayName() { return "Stapler request handling"; diff --git a/core/src/main/java/jenkins/telemetry/impl/UserLanguages.java b/core/src/main/java/jenkins/telemetry/impl/UserLanguages.java index 3856cea634..3d64e32cb7 100644 --- a/core/src/main/java/jenkins/telemetry/impl/UserLanguages.java +++ b/core/src/main/java/jenkins/telemetry/impl/UserLanguages.java @@ -31,7 +31,7 @@ import net.sf.json.JSONObject; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -55,25 +55,25 @@ public class UserLanguages extends Telemetry { private static final Map requestsByLanguage = new ConcurrentSkipListMap<>(); private static Logger LOGGER = Logger.getLogger(UserLanguages.class.getName()); - @Nonnull + @NonNull @Override public String getId() { return UserLanguages.class.getName(); } - @Nonnull + @NonNull @Override public String getDisplayName() { return "Browser languages"; } - @Nonnull + @NonNull @Override public LocalDate getStart() { return LocalDate.of(2018, 10, 1); } - @Nonnull + @NonNull @Override public LocalDate getEnd() { return LocalDate.of(2019, 1, 1); diff --git a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvent.java b/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvent.java index a8c1484fa2..ab13224f97 100644 --- a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvent.java +++ b/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvent.java @@ -24,7 +24,7 @@ package jenkins.telemetry.impl.java11; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.PrintWriter; import java.io.StringWriter; @@ -53,7 +53,7 @@ class MissingClassEvent { this.className = className; } - MissingClassEvent(@Nonnull String name, @Nonnull Throwable t) { + MissingClassEvent(@NonNull String name, @NonNull Throwable t) { this.className = name; StringWriter stackTrace = new StringWriter(); diff --git a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvents.java b/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvents.java index 563f655c72..2b7b2737e1 100644 --- a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvents.java +++ b/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvents.java @@ -26,7 +26,7 @@ package jenkins.telemetry.impl.java11; import com.google.common.annotations.VisibleForTesting; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -54,7 +54,7 @@ public class MissingClassEvents { * @return the occurrences stored for this throwable. 1 the fist time it's stored. > 1 for successive stores of the * same stack trace. 0 if we already stored MAX_EVENTS_PER_SEND (100) events for a single send. */ - public long put(String name, @Nonnull Throwable t) { + public long put(String name, @NonNull Throwable t) { // A final object to pass it to the function final AtomicLong occurrences = new AtomicLong(); @@ -92,7 +92,7 @@ public class MissingClassEvents { */ @VisibleForTesting - /* package */ synchronized @Nonnull ConcurrentHashMap, MissingClassEvent> getEventsAndClean() { + /* package */ synchronized @NonNull ConcurrentHashMap, MissingClassEvent> getEventsAndClean() { ConcurrentHashMap, MissingClassEvent> currentEvents = events; events = new ConcurrentHashMap<>(MAX_EVENTS_PER_SEND); return currentEvents; diff --git a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java b/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java index 2cd8aa6c9c..c962ca2b98 100644 --- a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java +++ b/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java @@ -26,6 +26,7 @@ package jenkins.telemetry.impl.java11; import com.google.common.annotations.VisibleForTesting; import hudson.Extension; +import hudson.util.VersionNumber; import io.jenkins.lib.versionnumber.JavaSpecificationVersion; import jenkins.model.Jenkins; import jenkins.telemetry.Telemetry; @@ -35,8 +36,8 @@ import net.sf.json.JSONObject; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.time.LocalDate; @@ -105,23 +106,24 @@ public class MissingClassTelemetry extends Telemetry { //hundreds when a job is created {"org.codehaus.groovy.control.ClassNodeResolver", "tryAsLoaderClassOrScript"}, {"org.kohsuke.stapler.RequestImpl$TypePair", "convertJSON"}, - {"net.bull.javamelody.FilterContext", "isMojarraAvailable"} // JENKINS-60725 + {"net.bull.javamelody.FilterContext", "isMojarraAvailable"}, // JENKINS-60725 + {"hudson.remoting.RemoteClassLoader$ClassLoaderProxy", "fetch3"} // JENKINS-61521 }; - @Nonnull + @NonNull @Override public String getDisplayName() { return "Missing classes related with Java updates"; } - @Nonnull + @NonNull @Override public LocalDate getStart() { return START; } - @Nonnull + @NonNull @Override public LocalDate getEnd() { return END; @@ -160,7 +162,8 @@ public class MissingClassTelemetry extends Telemetry { } JSONObject info = new JSONObject(); - info.put("core", Jenkins.getVersion() != null ? Jenkins.getVersion().toString() : "UNKNOWN"); + VersionNumber jenkinsVersion = Jenkins.getVersion(); + info.put("core", jenkinsVersion != null ? jenkinsVersion.toString() : "UNKNOWN"); info.put("clientDate", clientDateString()); info.put("classMissingEvents", events); @@ -172,7 +175,7 @@ public class MissingClassTelemetry extends Telemetry { * gather another window of events. * @return the map of missed classes events gathered along this window of telemetry */ - @Nonnull + @NonNull private JSONArray formatEventsAndInitialize() { // Save the current events and clean for next (not this one) telemetry send ConcurrentHashMap, MissingClassEvent> toReport = MissingClassTelemetry.events.getEventsAndClean(); @@ -191,8 +194,8 @@ public class MissingClassTelemetry extends Telemetry { * @param events events collected in this telemetry window. * @return the events formatted in a map. */ - @Nonnull - private JSONArray formatEvents(@Nonnull ConcurrentHashMap, MissingClassEvent> events) { + @NonNull + private JSONArray formatEvents(@NonNull ConcurrentHashMap, MissingClassEvent> events) { JSONArray jsonEvents = new JSONArray(); events.forEach((stackTrace, event) -> { @@ -213,7 +216,7 @@ public class MissingClassTelemetry extends Telemetry { * The current time in the same way as other telemetry implementations. * @return the UTC time formatted with the pattern yyyy-MM-dd'T'HH:mm'Z' */ - @Nonnull + @NonNull static String clientDateString() { TimeZone tz = TimeZone.getTimeZone("UTC"); DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); @@ -228,7 +231,7 @@ public class MissingClassTelemetry extends Telemetry { * @param name the name of the class * @param e the throwable to report if needed */ - public static void reportException(@Nonnull String name, @Nonnull Throwable e) { + public static void reportException(@NonNull String name, @NonNull Throwable e) { if (enabled()) { //ClassDefFoundError uses / instead of . name = name.replace('/', '.').trim(); @@ -248,7 +251,7 @@ public class MissingClassTelemetry extends Telemetry { * @param throwable The exception thrown * @return true if in the stack trace there is an ignored method / class. */ - private static boolean calledFromIgnoredPlace(@Nonnull Throwable throwable) { + private static boolean calledFromIgnoredPlace(@NonNull Throwable throwable) { for(String[] ignoredPlace : IGNORED_PLACES) { if (calledFrom(throwable, ignoredPlace[0], ignoredPlace[1])) { return true; @@ -264,7 +267,7 @@ public class MissingClassTelemetry extends Telemetry { * @param method method where the throwable was thrown in the clazz * @return true if the method of the clazz has thrown the throwable */ - private static boolean calledFrom (@Nonnull Throwable throwable, @Nonnull String clazz, @Nonnull String method){ + private static boolean calledFrom (@NonNull Throwable throwable, @NonNull String clazz, @NonNull String method){ StackTraceElement[] trace = throwable.getStackTrace(); for (StackTraceElement el : trace) { //If the exception has the class and method searched, it's called from there @@ -282,7 +285,7 @@ public class MissingClassTelemetry extends Telemetry { * {@link #reportExceptionInside(Throwable)} * @param e the exception to report if needed */ - private static void reportException(@Nonnull Throwable e) { + private static void reportException(@NonNull Throwable e) { if (enabled()) { String name = e.getMessage(); @@ -294,7 +297,7 @@ public class MissingClassTelemetry extends Telemetry { } } - private static boolean isFromMovedPackage(@Nonnull String clazz) { + private static boolean isFromMovedPackage(@NonNull String clazz) { for (String movedPackage : MOVED_PACKAGES) { if (clazz.startsWith(movedPackage)) { return true; @@ -308,7 +311,7 @@ public class MissingClassTelemetry extends Telemetry { * classes. * @param e the exception to look into */ - public static void reportExceptionInside(@Nonnull Throwable e) { + public static void reportExceptionInside(@NonNull Throwable e) { if (enabled()) { // Use a Set with equity based on == instead of equal to find cycles Set exceptionsReviewed = Collections.newSetFromMap(new IdentityHashMap<>()); @@ -323,7 +326,7 @@ public class MissingClassTelemetry extends Telemetry { * @param exceptionsReviewed the set of already reviewed exceptions * @return true if a exception was reported */ - private static boolean reportExceptionInside(@Nonnull Throwable e, @Nonnull Set exceptionsReviewed) { + private static boolean reportExceptionInside(@NonNull Throwable e, @NonNull Set exceptionsReviewed) { if (exceptionsReviewed.contains(e)) { LOGGER.log(Level.WARNING, CIRCULAR_REFERENCE, e); // Don't go deeper, we already did diff --git a/core/src/main/java/jenkins/tools/GlobalToolConfiguration.java b/core/src/main/java/jenkins/tools/GlobalToolConfiguration.java index dd6e9ce08b..1c371a6d5a 100644 --- a/core/src/main/java/jenkins/tools/GlobalToolConfiguration.java +++ b/core/src/main/java/jenkins/tools/GlobalToolConfiguration.java @@ -23,14 +23,13 @@ */ package jenkins.tools; -import com.google.common.base.Predicate; import hudson.Extension; import hudson.Functions; import hudson.model.Descriptor; import hudson.model.ManagementLink; import hudson.security.Permission; import hudson.util.FormApply; -import jenkins.model.GlobalConfigurationCategory; +import java.util.function.Predicate; import jenkins.model.Jenkins; import net.sf.json.JSONObject; @@ -40,6 +39,7 @@ import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.verb.POST; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import java.io.IOException; import java.util.logging.Level; @@ -71,7 +71,13 @@ public class GlobalToolConfiguration extends ManagementLink { @Override public Permission getRequiredPermission() { - return Jenkins.ADMINISTER; + return Jenkins.SYSTEM_READ; + } + + @NonNull + @Override + public Category getCategory() { + return Category.CONFIGURATION; } @POST @@ -81,12 +87,12 @@ public class GlobalToolConfiguration extends ManagementLink { FormApply.success(req.getContextPath() + "/manage").generateResponse(req, rsp, null); } - private boolean configure(StaplerRequest req, JSONObject json) throws hudson.model.Descriptor.FormException, IOException { + private boolean configure(StaplerRequest req, JSONObject json) throws Descriptor.FormException, IOException { Jenkins j = Jenkins.get(); j.checkPermission(Jenkins.ADMINISTER); boolean result = true; - for(Descriptor d : Functions.getSortedDescriptorsForGlobalConfig(FILTER)){ + for (Descriptor d : Functions.getSortedDescriptorsForGlobalConfigByDescriptor(FILTER)) { result &= configureDescriptor(req, json, d); } j.save(); @@ -101,11 +107,7 @@ public class GlobalToolConfiguration extends ManagementLink { return d.configure(req, js); } - public static Predicate FILTER = new Predicate() { - public boolean apply(GlobalConfigurationCategory input) { - return input instanceof ToolConfigurationCategory; - } - }; + public static Predicate FILTER = input -> input.getCategory() instanceof ToolConfigurationCategory; private static final Logger LOGGER = Logger.getLogger(GlobalToolConfiguration.class.getName()); } diff --git a/core/src/main/java/jenkins/triggers/ReverseBuildTrigger.java b/core/src/main/java/jenkins/triggers/ReverseBuildTrigger.java index b9161fd7e3..c0dbc11ebf 100644 --- a/core/src/main/java/jenkins/triggers/ReverseBuildTrigger.java +++ b/core/src/main/java/jenkins/triggers/ReverseBuildTrigger.java @@ -74,8 +74,8 @@ import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Like {@link BuildTrigger} but defined on the downstream project. @@ -181,7 +181,7 @@ public final class ReverseBuildTrigger extends Trigger implements Dependenc } } - @Override public void start(@Nonnull Job project, boolean newInstance) { + @Override public void start(@NonNull Job project, boolean newInstance) { super.start(project, newInstance); RunListenerImpl.get().invalidateCache(); } @@ -270,7 +270,7 @@ public final class ReverseBuildTrigger extends Trigger implements Dependenc } } - @Override public void onCompleted(@Nonnull Run r, @Nonnull TaskListener listener) { + @Override public void onCompleted(@NonNull Run r, @NonNull TaskListener listener) { Collection triggers; synchronized (this) { if (upstream2Trigger == null) { diff --git a/core/src/main/java/jenkins/triggers/SCMTriggerItem.java b/core/src/main/java/jenkins/triggers/SCMTriggerItem.java index d9b04d38cc..bfa8544ffb 100644 --- a/core/src/main/java/jenkins/triggers/SCMTriggerItem.java +++ b/core/src/main/java/jenkins/triggers/SCMTriggerItem.java @@ -37,8 +37,8 @@ import java.util.Collection; import java.util.Collections; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.model.ParameterizedJobMixIn; import jenkins.scm.SCMDecisionHandler; @@ -70,7 +70,7 @@ public interface SCMTriggerItem { * The implementation is responsible for checking the {@link SCMDecisionHandler} before proceeding * with the actual polling. */ - @Nonnull PollingResult poll(@Nonnull TaskListener listener); + @NonNull PollingResult poll(@NonNull TaskListener listener); @CheckForNull SCMTrigger getSCMTrigger(); @@ -79,7 +79,7 @@ public interface SCMTriggerItem { * May be used for informational purposes, or to determine whether to initiate polling. * @return a possibly empty collection */ - @Nonnull Collection getSCMs(); + @NonNull Collection getSCMs(); /** * Schedules a polling of this project. @@ -150,7 +150,7 @@ public interface SCMTriggerItem { } } - public static @Nonnull Collection resolveMultiScmIfConfigured(@CheckForNull SCM scm) { + public static @NonNull Collection resolveMultiScmIfConfigured(@CheckForNull SCM scm) { if (scm == null || scm instanceof NullSCM) { return Collections.emptySet(); } else if (scm.getClass().getName().equals("org.jenkinsci.plugins.multiplescms.MultiSCM")) { diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java index 6a2b5f8765..7843abb69a 100644 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ b/core/src/main/java/jenkins/util/AntClassLoader.java @@ -32,7 +32,8 @@ import org.apache.tools.ant.util.ReflectUtil; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -766,6 +767,30 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { return parent == null ? super.getResourceAsStream(name) : parent.getResourceAsStream(name); } + @FunctionalInterface + private interface Converter { + T convert(@NonNull JarFile jarFile, @NonNull JarEntry entry) throws IOException; + } + + @CheckForNull + private T storeAndConvert(JarFile jarFile, File file, String resourceName, Converter converter) throws IOException { + if (jarFile == null) { + if (file.exists()) { + jarFile = new JarFile(file); + jarFiles.put(file, jarFile); + } else { + return null; + } + //to eliminate a race condition, retrieve the entry + //that is in the hash table under that filename + jarFile = (JarFile) jarFiles.get(file); + } + JarEntry entry = jarFile.getJarEntry(resourceName); + if (entry == null) + return null; + return converter.convert(jarFile, entry); + } + /** * Returns an inputstream to a given resource in the given file which may * either be a directory or a zip file. @@ -787,21 +812,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { return Files.newInputStream(resource.toPath()); } } else { - if (jarFile == null) { - if (file.exists()) { - jarFile = new JarFile(file); - jarFiles.put(file, jarFile); - } else { - return null; - } - //to eliminate a race condition, retrieve the entry - //that is in the hash table under that filename - jarFile = (JarFile) jarFiles.get(file); - } - JarEntry entry = jarFile.getJarEntry(resourceName); - if (entry != null) { - return jarFile.getInputStream(entry); - } + return storeAndConvert(jarFile, file, resourceName, JarFile::getInputStream); } } catch (Exception e) { log("Ignoring Exception " + e.getClass().getName() + ": " + e.getMessage() @@ -1019,24 +1030,13 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { } } } else { - if (jarFile == null) { - if (file.exists()) { - jarFile = new JarFile(file); - jarFiles.put(file, jarFile); - } else { - return null; - } - // potential race-condition - jarFile = (JarFile) jarFiles.get(file); - } - JarEntry entry = jarFile.getJarEntry(resourceName); - if (entry != null) { + return storeAndConvert(jarFile, file, resourceName, (jar, entry) -> { try { return new URL("jar:" + FILE_UTILS.getFileURL(file) + "!/" + entry); } catch (MalformedURLException ex) { return null; } - } + }); } } catch (Exception e) { String msg = "Unable to obtain resource from " + file + ": "; diff --git a/core/src/main/java/jenkins/util/JSONSignatureValidator.java b/core/src/main/java/jenkins/util/JSONSignatureValidator.java index e873597ffa..5c8bc4d0ea 100644 --- a/core/src/main/java/jenkins/util/JSONSignatureValidator.java +++ b/core/src/main/java/jenkins/util/JSONSignatureValidator.java @@ -78,7 +78,9 @@ public class JSONSignatureValidator { } catch (CertificateNotYetValidException e) { warning = FormValidation.warning(e, String.format("Certificate %s is not yet valid in %s", cert.toString(), name)); } - LOGGER.log(Level.FINE, "Add certificate found in json doc: \r\n\tsubjectDN: {0}\r\n\tissuer: {1}", new Object[]{c.getSubjectDN(), c.getIssuerDN()}); + LOGGER.log(Level.FINE, "Add certificate found in JSON document:\n\tsubjectDN: {0}\n\tissuer: {1}\n\tnotBefore: {2}\n\tnotAfter: {3}", + new Object[] { c.getSubjectDN(), c.getIssuerDN(), c.getNotBefore(), c.getNotAfter() }); + LOGGER.log(Level.FINEST, () -> "Certificate from JSON document: " + c); certs.add(c); } catch (IllegalArgumentException ex) { throw new IOException("Could not decode certificate", ex); @@ -253,6 +255,12 @@ public class JSONSignatureValidator { try (InputStream in = j.servletContext.getResourceAsStream(cert)) { if (in == null) continue; // our test for paths ending in / should prevent this from happening certificate = cf.generateCertificate(in); + if (certificate instanceof X509Certificate) { + X509Certificate c = (X509Certificate) certificate; + LOGGER.log(Level.FINE, "Add CA certificate found in webapp resources:\n\tsubjectDN: {0}\n\tissuer: {1}\n\tnotBefore: {2}\n\tnotAfter: {3}", + new Object[] { c.getSubjectDN(), c.getIssuerDN(), c.getNotBefore(), c.getNotAfter() }); + } + LOGGER.log(Level.FINEST, () -> "CA certificate from webapp resource " + cert + ": " + certificate); } catch (CertificateException e) { LOGGER.log(Level.WARNING, String.format("Webapp resources in /WEB-INF/update-center-rootCAs are " + "expected to be either certificates or .txt files documenting the " @@ -263,8 +271,6 @@ public class JSONSignatureValidator { } try { TrustAnchor certificateAuthority = new TrustAnchor((X509Certificate) certificate, null); - LOGGER.log(Level.FINE, "Add Certificate Authority {0}: {1}", - new Object[]{cert, (certificateAuthority.getTrustedCert() == null ? null : certificateAuthority.getTrustedCert().getSubjectDN())}); anchors.add(certificateAuthority); } catch (IllegalArgumentException e) { LOGGER.log(Level.WARNING, @@ -282,6 +288,12 @@ public class JSONSignatureValidator { Certificate certificate; try (InputStream in = Files.newInputStream(cert.toPath())) { certificate = cf.generateCertificate(in); + if (certificate instanceof X509Certificate) { + X509Certificate c = (X509Certificate) certificate; + LOGGER.log(Level.FINE, "Add CA certificate found in Jenkins home:\n\tsubjectDN: {0}\n\tissuer: {1}\n\tnotBefore: {2}\n\tnotAfter: {3}", + new Object[] { c.getSubjectDN(), c.getIssuerDN(), c.getNotBefore(), c.getNotAfter() }); + } + LOGGER.log(Level.FINEST, () -> "CA certificate from Jenkins home " + cert + ": " + certificate); } catch (InvalidPathException e) { throw new IOException(e); } catch (CertificateException e) { @@ -294,8 +306,6 @@ public class JSONSignatureValidator { } try { TrustAnchor certificateAuthority = new TrustAnchor((X509Certificate) certificate, null); - LOGGER.log(Level.FINE, "Add Certificate Authority {0}: {1}", - new Object[]{cert, (certificateAuthority.getTrustedCert() == null ? null : certificateAuthority.getTrustedCert().getSubjectDN())}); anchors.add(certificateAuthority); } catch (IllegalArgumentException e) { LOGGER.log(Level.WARNING, diff --git a/core/src/main/java/jenkins/util/ProgressiveRendering.java b/core/src/main/java/jenkins/util/ProgressiveRendering.java index cdf2055e76..137ca68b63 100644 --- a/core/src/main/java/jenkins/util/ProgressiveRendering.java +++ b/core/src/main/java/jenkins/util/ProgressiveRendering.java @@ -38,7 +38,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.http.HttpServletRequest; import net.sf.json.JSON; import net.sf.json.JSONObject; @@ -224,7 +224,7 @@ public abstract class ProgressiveRendering { * on the same monitor such as {@code this}. * @return any JSON data you like */ - protected abstract @Nonnull JSON data(); + protected abstract @NonNull JSON data(); /** * Indicate what portion of the work has been done. diff --git a/core/src/main/java/jenkins/util/ResourceBundleUtil.java b/core/src/main/java/jenkins/util/ResourceBundleUtil.java index b2eee336ab..64a650c0cf 100644 --- a/core/src/main/java/jenkins/util/ResourceBundleUtil.java +++ b/core/src/main/java/jenkins/util/ResourceBundleUtil.java @@ -27,8 +27,8 @@ import net.sf.json.JSONObject; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.PluginWrapper; import java.util.logging.Logger; import java.util.Locale; @@ -58,7 +58,7 @@ public class ResourceBundleUtil { * @return The bundle JSON. * @throws MissingResourceException Missing resource bundle. */ - public static @Nonnull JSONObject getBundle(@Nonnull String baseName) throws MissingResourceException { + public static @NonNull JSONObject getBundle(@NonNull String baseName) throws MissingResourceException { return getBundle(baseName, Locale.getDefault()); } @@ -69,7 +69,7 @@ public class ResourceBundleUtil { * @return The bundle JSON. * @throws MissingResourceException Missing resource bundle. */ - public static @Nonnull JSONObject getBundle(@Nonnull String baseName, @Nonnull Locale locale) throws MissingResourceException { + public static @NonNull JSONObject getBundle(@NonNull String baseName, @NonNull Locale locale) throws MissingResourceException { String bundleKey = baseName + ":" + locale.toString(); JSONObject bundleJSON = bundles.get(bundleKey); @@ -109,7 +109,7 @@ public class ResourceBundleUtil { * @param classLoader The classLoader * @return The bundle JSON. */ - private static @CheckForNull ResourceBundle getBundle(@Nonnull String baseName, @Nonnull Locale locale, @Nonnull ClassLoader classLoader) { + private static @CheckForNull ResourceBundle getBundle(@NonNull String baseName, @NonNull Locale locale, @NonNull ClassLoader classLoader) { try { return ResourceBundle.getBundle(baseName, locale, classLoader); } catch (MissingResourceException e) { @@ -125,7 +125,7 @@ public class ResourceBundleUtil { * @param bundle The resource bundle. * @return The bundle JSON. */ - private static JSONObject toJSONObject(@Nonnull ResourceBundle bundle) { + private static JSONObject toJSONObject(@NonNull ResourceBundle bundle) { JSONObject json = new JSONObject(); for (String key : bundle.keySet()) { json.put(key, bundle.getString(key)); diff --git a/core/src/main/java/jenkins/util/SystemProperties.java b/core/src/main/java/jenkins/util/SystemProperties.java index ef79a782eb..248d6ae8eb 100644 --- a/core/src/main/java/jenkins/util/SystemProperties.java +++ b/core/src/main/java/jenkins/util/SystemProperties.java @@ -39,8 +39,8 @@ import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; @@ -87,7 +87,7 @@ public class SystemProperties { private static final Handler NULL_HANDLER = key -> null; - private static @Nonnull Handler handler = NULL_HANDLER; + private static @NonNull Handler handler = NULL_HANDLER; // declared in WEB-INF/web.xml public static final class Listener implements ServletContextListener, OnMaster { @@ -183,26 +183,7 @@ public class SystemProperties { */ @CheckForNull public static String getString(String key) { - String value = System.getProperty(key); // keep passing on any exceptions - if (value != null) { - if (LOGGER.isLoggable(Level.CONFIG)) { - LOGGER.log(Level.CONFIG, "Property (system): {0} => {1}", new Object[] {key, value}); - } - return value; - } - - value = handler.getString(key); - if (value != null) { - if (LOGGER.isLoggable(Level.CONFIG)) { - LOGGER.log(Level.CONFIG, "Property (context): {0} => {1}", new Object[]{key, value}); - } - return value; - } - - if (LOGGER.isLoggable(Level.CONFIG)) { - LOGGER.log(Level.CONFIG, "Property (not found): {0}", key); - } - return null; + return getString(key, null); } /** diff --git a/core/src/main/java/jenkins/util/TimeDuration.java b/core/src/main/java/jenkins/util/TimeDuration.java index 8b0fff3f5e..1422ffafe4 100644 --- a/core/src/main/java/jenkins/util/TimeDuration.java +++ b/core/src/main/java/jenkins/util/TimeDuration.java @@ -4,7 +4,7 @@ import org.apache.commons.beanutils.Converter; import org.kohsuke.stapler.QueryParameter; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; /** * Represents a length of something, like {@code 3sec}. diff --git a/core/src/main/java/jenkins/util/Timer.java b/core/src/main/java/jenkins/util/Timer.java index 7451bf6892..3dd80b2166 100644 --- a/core/src/main/java/jenkins/util/Timer.java +++ b/core/src/main/java/jenkins/util/Timer.java @@ -4,7 +4,7 @@ import hudson.security.ACL; import hudson.util.ClassLoaderSanityThreadFactory; import hudson.util.DaemonThreadFactory; import hudson.util.NamingThreadFactory; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.concurrent.ScheduledExecutorService; import jenkins.security.ImpersonatingScheduledExecutorService; @@ -38,7 +38,7 @@ public class Timer { * * @return the single {@link ScheduledExecutorService}. */ - @Nonnull + @NonNull public static synchronized ScheduledExecutorService get() { if (executorService == null) { // corePoolSize is set to 10, but will only be created if needed. diff --git a/core/src/main/java/jenkins/util/VirtualFile.java b/core/src/main/java/jenkins/util/VirtualFile.java index 80a747143b..8cec34a569 100644 --- a/core/src/main/java/jenkins/util/VirtualFile.java +++ b/core/src/main/java/jenkins/util/VirtualFile.java @@ -54,8 +54,8 @@ import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.MasterToSlaveFileCallable; import jenkins.model.ArtifactManager; @@ -114,7 +114,7 @@ public abstract class VirtualFile implements Comparable, Serializab * For a “root directory” this may be the empty string. * @return a simple name (no slashes) */ - public abstract @Nonnull String getName(); + public abstract @NonNull String getName(); /** * Gets a URI. @@ -123,7 +123,7 @@ public abstract class VirtualFile implements Comparable, Serializab * unless some sort of authentication is also embedded. * @return a URI (need not be absolute) */ - public abstract @Nonnull URI toURI(); + public abstract @NonNull URI toURI(); /** * Gets the parent file. @@ -173,7 +173,7 @@ public abstract class VirtualFile implements Comparable, Serializab * @return a list of children (files and subdirectories); empty for a file or nonexistent directory * @throws IOException if this directory exists but listing was not possible for some other reason */ - public abstract @Nonnull VirtualFile[] list() throws IOException; + public abstract @NonNull VirtualFile[] list() throws IOException; @Restricted(NoExternalUse.class) public boolean supportsQuickRecursiveListing() { @@ -188,7 +188,7 @@ public abstract class VirtualFile implements Comparable, Serializab * @throws IOException if this directory exists but listing was not possible for some other reason */ @Restricted(NoExternalUse.class) - public @Nonnull List listOnlyDescendants() throws IOException { + public @NonNull List listOnlyDescendants() throws IOException { VirtualFile[] children = list(); List result = new ArrayList<>(); for (VirtualFile child : children) { @@ -203,7 +203,7 @@ public abstract class VirtualFile implements Comparable, Serializab * @deprecated use {@link #list(String, String, boolean)} instead */ @Deprecated - public @Nonnull String[] list(String glob) throws IOException { + public @NonNull String[] list(String glob) throws IOException { return list(glob.replace('\\', '/'), null, true).toArray(MemoryReductionUtil.EMPTY_STRING_ARRAY); } @@ -220,7 +220,7 @@ public abstract class VirtualFile implements Comparable, Serializab * @since 2.118 */ @Restricted(Beta.class) - public @Nonnull Collection list(@Nonnull String includes, @CheckForNull String excludes, boolean useDefaultExcludes) throws IOException { + public @NonNull Collection list(@NonNull String includes, @CheckForNull String excludes, boolean useDefaultExcludes) throws IOException { Collection r = run(new CollectFiles(this)); List includePatterns = patterns(includes); List excludePatterns = patterns(excludes); @@ -274,7 +274,7 @@ public abstract class VirtualFile implements Comparable, Serializab * @param name a relative path, possibly including {@code /} (but not {@code ..}) * @return a representation of that child, whether it actually exists or not */ - public abstract @Nonnull VirtualFile child(@Nonnull String name); + public abstract @NonNull VirtualFile child(@NonNull String name); /** * Gets the file length. @@ -479,7 +479,7 @@ public abstract class VirtualFile implements Comparable, Serializab return true; } - @Override public @Nonnull List listOnlyDescendants() throws IOException { + @Override public @NonNull List listOnlyDescendants() throws IOException { if (isIllegalSymlink()) { return Collections.emptyList(); } @@ -699,7 +699,7 @@ public abstract class VirtualFile implements Comparable, Serializab return this.f.getChannel() == FilePath.localChannel; } - @Override public @Nonnull List listOnlyDescendants() throws IOException { + @Override public @NonNull List listOnlyDescendants() throws IOException { try { if (!isDescendant("")) { return Collections.emptyList(); diff --git a/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java b/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java index 638992fc20..51b2ed9647 100644 --- a/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java +++ b/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java @@ -12,7 +12,7 @@ import java.util.Set; import java.util.TreeSet; import static java.util.logging.Level.WARNING; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletContext; import jenkins.model.Jenkins; @@ -53,7 +53,7 @@ public class GroovyHookScript { this(hook, j.servletContext, j.getRootDir(), j.getPluginManager().uberClassLoader); } - public GroovyHookScript(String hook, @Nonnull ServletContext servletContext, @Nonnull File home, @Nonnull ClassLoader loader) { + public GroovyHookScript(String hook, @NonNull ServletContext servletContext, @NonNull File home, @NonNull ClassLoader loader) { this.hook = hook; this.servletContext = servletContext; this.home = home; diff --git a/core/src/main/java/jenkins/util/io/CompositeIOException.java b/core/src/main/java/jenkins/util/io/CompositeIOException.java index 04090859f4..d1ff484c91 100644 --- a/core/src/main/java/jenkins/util/io/CompositeIOException.java +++ b/core/src/main/java/jenkins/util/io/CompositeIOException.java @@ -27,22 +27,53 @@ package jenkins.util.io; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.io.UncheckedIOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @Restricted(NoExternalUse.class) public class CompositeIOException extends IOException { + private static final long serialVersionUID = 121943141387608148L; + + /** + * The maximum number of exceptions that can be reported by a single + * {@code CompositeIOException}. + *

+ * The number of exceptions is limited to avoid pathological cases where + * a huge number of exceptions could lead to excessive memory usage. + * For example, if the number of exceptions was unlimited, a call to + * {@code Util.deleteRecursive} could fail with a + * {@code CompositeIOException} that contains an exception for every + * single file inside of the directory. + */ + public static final int EXCEPTION_LIMIT = 10; + private final List exceptions; - public CompositeIOException(String message, @Nonnull List exceptions) { - super(message); - this.exceptions = exceptions; - exceptions.forEach(this::addSuppressed); + /** + * Construct a new {@code CompositeIOException} where the given list of + * exceptions are added as suppressed exceptions to the new exception. + *

+ * If the given list of exceptions is longer than {@link #EXCEPTION_LIMIT}, + * the list will be truncated to that length, and a message indicating the + * number of discarded exceptions will be appended to the original message. + */ + public CompositeIOException(String message, @NonNull List exceptions) { + super(message + getDiscardedExceptionsMessage(exceptions)); + if (exceptions.size() > EXCEPTION_LIMIT) { + this.exceptions = new ArrayList<>(exceptions.subList(0, EXCEPTION_LIMIT)); + } else { + this.exceptions = exceptions; + } + this.exceptions.forEach(this::addSuppressed); } + /** + * @see CompositeIOException(String, List) + */ public CompositeIOException(String message, IOException... exceptions) { this(message, Arrays.asList(exceptions)); } @@ -54,4 +85,12 @@ public class CompositeIOException extends IOException { public UncheckedIOException asUncheckedIOException() { return new UncheckedIOException(this); } + + private static String getDiscardedExceptionsMessage(List exceptions) { + if (exceptions.size() > EXCEPTION_LIMIT) { + return " (Discarded " + (exceptions.size() - EXCEPTION_LIMIT) + " additional exceptions)"; + } else { + return ""; + } + } } diff --git a/core/src/main/java/jenkins/util/io/LinesStream.java b/core/src/main/java/jenkins/util/io/LinesStream.java index 0925b34929..92ff49cb01 100644 --- a/core/src/main/java/jenkins/util/io/LinesStream.java +++ b/core/src/main/java/jenkins/util/io/LinesStream.java @@ -35,8 +35,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Iterator; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; /** * Represents a stream over the lines of a text file. @@ -63,7 +63,7 @@ import javax.annotation.Nullable; @CleanupObligation public class LinesStream implements Closeable, Iterable { - private final @Nonnull BufferedReader in; + private final @NonNull BufferedReader in; private transient @Nullable Iterator iterator; /** @@ -73,7 +73,7 @@ public class LinesStream implements Closeable, Iterable { * @throws IOException if the file at {@code path} cannot be opened for * reading. */ - public LinesStream(@Nonnull Path path) throws IOException { + public LinesStream(@NonNull Path path) throws IOException { in = Files.newBufferedReader(path); // uses UTF-8 by default } diff --git a/core/src/main/java/jenkins/util/io/PathRemover.java b/core/src/main/java/jenkins/util/io/PathRemover.java index 570a8e1877..045592d922 100644 --- a/core/src/main/java/jenkins/util/io/PathRemover.java +++ b/core/src/main/java/jenkins/util/io/PathRemover.java @@ -40,7 +40,7 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -56,23 +56,23 @@ public class PathRemover { return new PathRemover(ignored -> false, PathChecker.ALLOW_ALL); } - public static PathRemover newRemoverWithStrategy(@Nonnull RetryStrategy retryStrategy) { + public static PathRemover newRemoverWithStrategy(@NonNull RetryStrategy retryStrategy) { return new PathRemover(retryStrategy, PathChecker.ALLOW_ALL); } - public static PathRemover newFilteredRobustRemover(@Nonnull PathChecker pathChecker, int maxRetries, boolean gcAfterFailedRemove, long waitBetweenRetries) { + public static PathRemover newFilteredRobustRemover(@NonNull PathChecker pathChecker, int maxRetries, boolean gcAfterFailedRemove, long waitBetweenRetries) { return new PathRemover(new PausingGCRetryStrategy(Math.max(maxRetries, 0), gcAfterFailedRemove, waitBetweenRetries), pathChecker); } private final RetryStrategy retryStrategy; private final PathChecker pathChecker; - private PathRemover(@Nonnull RetryStrategy retryStrategy, @Nonnull PathChecker pathChecker) { + private PathRemover(@NonNull RetryStrategy retryStrategy, @NonNull PathChecker pathChecker) { this.retryStrategy = retryStrategy; this.pathChecker = pathChecker; } - public void forceRemoveFile(@Nonnull Path path) throws IOException { + public void forceRemoveFile(@NonNull Path path) throws IOException { for (int retryAttempts = 0; ; retryAttempts++) { Optional maybeError = tryRemoveFile(path); if (!maybeError.isPresent()) return; @@ -82,7 +82,7 @@ public class PathRemover { } } - public void forceRemoveDirectoryContents(@Nonnull Path path) throws IOException { + public void forceRemoveDirectoryContents(@NonNull Path path) throws IOException { for (int retryAttempt = 0; ; retryAttempt++) { List errors = tryRemoveDirectoryContents(path); if (errors.isEmpty()) return; @@ -91,7 +91,7 @@ public class PathRemover { } } - public void forceRemoveRecursive(@Nonnull Path path) throws IOException { + public void forceRemoveRecursive(@NonNull Path path) throws IOException { for (int retryAttempt = 0; ; retryAttempt++) { List errors = tryRemoveRecursive(path); if (errors.isEmpty()) return; @@ -103,7 +103,7 @@ public class PathRemover { @Restricted(NoExternalUse.class) @FunctionalInterface public interface PathChecker { - void check(@Nonnull Path path) throws SecurityException; + void check(@NonNull Path path) throws SecurityException; PathChecker ALLOW_ALL = path -> {}; } @@ -113,7 +113,7 @@ public class PathRemover { public interface RetryStrategy { boolean shouldRetry(int retriesAttempted); - default String failureMessage(@Nonnull Path fileToRemove, int retryCount) { + default String failureMessage(@NonNull Path fileToRemove, int retryCount) { StringBuilder sb = new StringBuilder() .append("Unable to delete '") .append(fileToRemove) @@ -165,7 +165,7 @@ public class PathRemover { } @Override - public String failureMessage(@Nonnull Path fileToRemove, int retryCount) { + public String failureMessage(@NonNull Path fileToRemove, int retryCount) { StringBuilder sb = new StringBuilder(); sb.append("Unable to delete '"); sb.append(fileToRemove); @@ -200,7 +200,7 @@ public class PathRemover { } } - private Optional tryRemoveFile(@Nonnull Path path) { + private Optional tryRemoveFile(@NonNull Path path) { try { removeOrMakeRemovableThenRemove(path.normalize()); return Optional.empty(); @@ -209,7 +209,7 @@ public class PathRemover { } } - private List tryRemoveRecursive(@Nonnull Path path) { + private List tryRemoveRecursive(@NonNull Path path) { Path normalized = path.normalize(); List accumulatedErrors = Util.isSymlink(normalized) ? new ArrayList<>() : tryRemoveDirectoryContents(normalized); @@ -217,7 +217,7 @@ public class PathRemover { return accumulatedErrors; } - private List tryRemoveDirectoryContents(@Nonnull Path path) { + private List tryRemoveDirectoryContents(@NonNull Path path) { Path normalized = path.normalize(); List accumulatedErrors = new ArrayList<>(); if (!Files.isDirectory(normalized)) return accumulatedErrors; @@ -231,7 +231,7 @@ public class PathRemover { return accumulatedErrors; } - private void removeOrMakeRemovableThenRemove(@Nonnull Path path) throws IOException { + private void removeOrMakeRemovableThenRemove(@NonNull Path path) throws IOException { pathChecker.check(path); try { Files.deleteIfExists(path); @@ -254,7 +254,7 @@ public class PathRemover { } } - private static void makeRemovable(@Nonnull Path path) throws IOException { + private static void makeRemovable(@NonNull Path path) throws IOException { if (!Files.isWritable(path)) { makeWritable(path); } @@ -277,7 +277,7 @@ public class PathRemover { } } - private static void makeWritable(@Nonnull Path path) throws IOException { + private static void makeWritable(@NonNull Path path) throws IOException { if (!Functions.isWindows()) { try { PosixFileAttributes attrs = Files.readAttributes(path, PosixFileAttributes.class); diff --git a/core/src/main/java/jenkins/util/xml/XMLUtils.java b/core/src/main/java/jenkins/util/xml/XMLUtils.java index a47abf19f2..cc672bb8ee 100644 --- a/core/src/main/java/jenkins/util/xml/XMLUtils.java +++ b/core/src/main/java/jenkins/util/xml/XMLUtils.java @@ -18,7 +18,7 @@ import java.nio.charset.Charset; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -55,7 +55,7 @@ public final class XMLUtils { * {@link SAXSource} in order to be able to prevent XXE attacks. * @param out The Result of transforming the {@code source}. */ - public static void safeTransform(@Nonnull Source source, @Nonnull Result out) throws TransformerException, + public static void safeTransform(@NonNull Source source, @NonNull Result out) throws TransformerException, SAXException { InputSource src = SAXSource.sourceToInputSource(source); @@ -110,7 +110,7 @@ public final class XMLUtils { * @throws IOException Error reading from the steam. * @since 2.0 */ - public static @Nonnull Document parse(@Nonnull Reader stream) throws SAXException, IOException { + public static @NonNull Document parse(@NonNull Reader stream) throws SAXException, IOException { DocumentBuilder docBuilder; try { @@ -132,7 +132,7 @@ public final class XMLUtils { * @throws IOException Error reading from the file. * @since 2.0 */ - public static @Nonnull Document parse(@Nonnull File file, @Nonnull String encoding) throws SAXException, IOException { + public static @NonNull Document parse(@NonNull File file, @NonNull String encoding) throws SAXException, IOException { if (!file.exists() || !file.isFile()) { throw new IllegalArgumentException(String.format("File %s does not exist or is not a 'normal' file.", file.getAbsolutePath())); } @@ -159,7 +159,7 @@ public final class XMLUtils { * @throws XPathExpressionException Invalid XPath expression. * @since 2.0 */ - public static @Nonnull String getValue(@Nonnull String xpath, @Nonnull File file) throws IOException, SAXException, XPathExpressionException { + public static @NonNull String getValue(@NonNull String xpath, @NonNull File file) throws IOException, SAXException, XPathExpressionException { return getValue(xpath, file, Charset.defaultCharset().toString()); } @@ -175,7 +175,7 @@ public final class XMLUtils { * @throws XPathExpressionException Invalid XPath expression. * @since 2.0 */ - public static @Nonnull String getValue(@Nonnull String xpath, @Nonnull File file, @Nonnull String fileDataEncoding) throws IOException, SAXException, XPathExpressionException { + public static @NonNull String getValue(@NonNull String xpath, @NonNull File file, @NonNull String fileDataEncoding) throws IOException, SAXException, XPathExpressionException { Document document = parse(file, fileDataEncoding); return getValue(xpath, document); } diff --git a/core/src/main/java/jenkins/websocket/WebSocketSession.java b/core/src/main/java/jenkins/websocket/WebSocketSession.java index 95a7ed7609..78400a77a7 100644 --- a/core/src/main/java/jenkins/websocket/WebSocketSession.java +++ b/core/src/main/java/jenkins/websocket/WebSocketSession.java @@ -39,7 +39,7 @@ import org.kohsuke.accmod.restrictions.Beta; /** * One WebSocket connection. * @see WebSockets - * @since TODO + * @since 2.216 */ @Restricted(Beta.class) public abstract class WebSocketSession { @@ -60,6 +60,7 @@ public abstract class WebSocketSession { private static final Logger LOGGER = Logger.getLogger(WebSocketSession.class.getName()); private Object session; + // https://www.eclipse.org/jetty/javadoc/9.4.24.v20191120/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpoint.html private Object remoteEndpoint; private ScheduledFuture pings; @@ -130,6 +131,15 @@ public abstract class WebSocketSession { } } + @SuppressWarnings("unchecked") + protected final void sendBinary(ByteBuffer partialByte, boolean isLast) { + try { + remoteEndpoint.getClass().getMethod("sendPartialBytes", ByteBuffer.class, boolean.class).invoke(remoteEndpoint, partialByte, isLast); + } catch (Exception x) { + throw new RuntimeException(x); + } + } + @SuppressWarnings("unchecked") protected final Future sendText(String text) { try { diff --git a/core/src/main/java/jenkins/websocket/WebSockets.java b/core/src/main/java/jenkins/websocket/WebSockets.java index 2d856d5069..5410fba7bb 100644 --- a/core/src/main/java/jenkins/websocket/WebSockets.java +++ b/core/src/main/java/jenkins/websocket/WebSockets.java @@ -41,7 +41,7 @@ import org.kohsuke.stapler.Stapler; /** * Support for serving WebSocket responses. - * @since TODO + * @since 2.216 */ @Restricted(Beta.class) @Extension diff --git a/core/src/main/java/jenkins/widgets/HistoryPageEntry.java b/core/src/main/java/jenkins/widgets/HistoryPageEntry.java index b3aeaecedb..9c11dceb7f 100644 --- a/core/src/main/java/jenkins/widgets/HistoryPageEntry.java +++ b/core/src/main/java/jenkins/widgets/HistoryPageEntry.java @@ -26,7 +26,7 @@ package jenkins.widgets; import hudson.model.Queue; import hudson.model.Run; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Represents an entry used by the {@link HistoryPageFilter}. @@ -54,7 +54,7 @@ public class HistoryPageEntry { return getEntryId(entry); } - protected static long getEntryId(@Nonnull Object entry) { + protected static long getEntryId(@NonNull Object entry) { if (entry instanceof Queue.Item) { return ((Queue.Item) entry).getId(); } else if (entry instanceof Run) { diff --git a/core/src/main/java/jenkins/widgets/HistoryPageFilter.java b/core/src/main/java/jenkins/widgets/HistoryPageFilter.java index 5f01c557c2..7ff9b94453 100644 --- a/core/src/main/java/jenkins/widgets/HistoryPageFilter.java +++ b/core/src/main/java/jenkins/widgets/HistoryPageFilter.java @@ -34,7 +34,7 @@ import hudson.search.UserSearchProperty; import hudson.util.Iterators; import hudson.widgets.HistoryWidget; -import javax.annotation.Nonnull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; @@ -102,7 +102,7 @@ public class HistoryPageFilter { * Set the search string used to narrow the filtered set of builds. * @param searchString The search string. */ - public void setSearchString(@Nonnull String searchString) { + public void setSearchString(@NonNull String searchString) { this.searchString = searchString; } @@ -113,7 +113,7 @@ public class HistoryPageFilter { * @deprecated Replaced by add(Iterable<T>) as of version 2.15 */ @Deprecated - public void add(@Nonnull List runItems) { + public void add(@NonNull List runItems) { addInternal(runItems); } @@ -123,7 +123,7 @@ public class HistoryPageFilter { * @param runItems The items to be added. Assumes the items are in descending queue ID order i.e. newest first. * @since 2.17 */ - public void add(@Nonnull Iterable runItems) { + public void add(@NonNull Iterable runItems) { addInternal(runItems); } @@ -134,7 +134,7 @@ public class HistoryPageFilter { * @param queueItems The queue items to be added. Queue items do not need to be sorted. * @since 2.17 */ - public void add(@Nonnull Iterable runItems, @Nonnull List queueItems) { + public void add(@NonNull Iterable runItems, @NonNull List queueItems) { sort(queueItems); addInternal(Iterables.concat(queueItems, runItems)); } @@ -144,7 +144,7 @@ public class HistoryPageFilter { * @param items The items to be added. * @param The type of items should either be T or Queue.Item. */ - private void addInternal(@Nonnull Iterable items) { + private void addInternal(@NonNull Iterable items) { // Note that items can be a large lazily evaluated collection, // so this method is optimized to only iterate through it as much as needed. @@ -245,7 +245,7 @@ public class HistoryPageFilter { }); } - private long getNextBuildNumber(@Nonnull Object entry) { + private long getNextBuildNumber(@NonNull Object entry) { if (entry instanceof Queue.Item) { Queue.Task task = ((Queue.Item) entry).task; if (task instanceof Job) { @@ -316,7 +316,7 @@ public class HistoryPageFilter { return Math.max(0, (maxEntries - size())); } - private boolean fitsSearchParams(@Nonnull Queue.Item item) { + private boolean fitsSearchParams(@NonNull Queue.Item item) { if (fitsSearchString(item.getDisplayName())) { return true; } else if (fitsSearchString(item.getId())) { @@ -326,7 +326,7 @@ public class HistoryPageFilter { return false; } - private boolean fitsSearchParams(@Nonnull Run run) { + private boolean fitsSearchParams(@NonNull Run run) { if (searchString == null) { return true; } diff --git a/core/src/main/resources/META-INF/upgrade/JSR-305.hint b/core/src/main/resources/META-INF/upgrade/JSR-305.hint new file mode 100644 index 0000000000..cc030286af --- /dev/null +++ b/core/src/main/resources/META-INF/upgrade/JSR-305.hint @@ -0,0 +1,3 @@ +javax.annotation.CheckForNull => edu.umd.cs.findbugs.annotations.CheckForNull;; +javax.annotation.Nonnull => edu.umd.cs.findbugs.annotations.NonNull;; +javax.annotation.Nullable => edu.umd.cs.findbugs.annotations.Nullable;; \ No newline at end of file diff --git a/core/src/main/resources/hudson/AboutJenkins/index.jelly b/core/src/main/resources/hudson/AboutJenkins/index.jelly index 51d7b09003..c32fdb2602 100644 --- a/core/src/main/resources/hudson/AboutJenkins/index.jelly +++ b/core/src/main/resources/hudson/AboutJenkins/index.jelly @@ -26,8 +26,7 @@ THE SOFTWARE. - - +

@@ -56,7 +55,7 @@ THE SOFTWARE.
' + i18n('detached-possible-dependents') + '
'); return true; } @@ -327,7 +325,7 @@ Behaviour.specify("#filter-box", '_table', 0, function(e) { return true; } - if (pluginTR.hasClassName('detached')) { + if (pluginTR.hasClassName('possibly-has-implied-dependents')) { infoContainer.update('
' + i18n('detached-uninstall') + '
' + i18n('detached-possible-dependents') + '
'); return true; } @@ -428,3 +426,7 @@ Behaviour.specify("#filter-box", '_table', 0, function(e) { setEnableWidgetStates(); }); }()); + +Element.observe(window, "load", function() { + document.getElementById('filter-box').focus(); +}); \ No newline at end of file diff --git a/core/src/main/resources/hudson/PluginManager/advanced.jelly b/core/src/main/resources/hudson/PluginManager/advanced.jelly index 9c06363f03..e4e9e201d1 100644 --- a/core/src/main/resources/hudson/PluginManager/advanced.jelly +++ b/core/src/main/resources/hudson/PluginManager/advanced.jelly @@ -26,80 +26,97 @@ THE SOFTWARE. List of available new plugins --> - - - - - -
- - - - -
- -

${%HTTP Proxy Configuration}

- - - - - - - - - - -
+ + + + + + + + + + + + + +
+
+
+

${%HTTP Proxy Configuration}

+ + + + + + + + + + + + +
- -

${%Upload Plugin}

- - -
- ${%uploadtext} -
-
- - - ${%File}: - - - - -
-
- -

${%Update Site}

- - - - - - - - - - - - - - - -

${%Other Sites}

-
    - - -
  • ${site.url}
  • + +
    +

    ${%Upload Plugin}

    + + +
    + ${%uploadtext} +
    +
    + + + ${%File}: + + + + + +
    +
    +
    + +
    +

    ${%Update Site}

    + + + + + + + + + + + + + + + + +
    + + +
    +

    ${%Other Sites}

    +
      + + +
    • ${site.url}
    • +
      +
      +
    +
    -
    -
-
-
-
-
-
- -
-
-
+
+ +
+ +
+ + diff --git a/core/src/main/resources/hudson/PluginManager/available.jelly b/core/src/main/resources/hudson/PluginManager/available.jelly index 8a029af350..83d0485346 100644 --- a/core/src/main/resources/hudson/PluginManager/available.jelly +++ b/core/src/main/resources/hudson/PluginManager/available.jelly @@ -27,5 +27,5 @@ THE SOFTWARE. --> - + diff --git a/core/src/main/resources/hudson/PluginManager/check.jelly b/core/src/main/resources/hudson/PluginManager/check.jelly index 3dcdd06e6d..24cd04a801 100644 --- a/core/src/main/resources/hudson/PluginManager/check.jelly +++ b/core/src/main/resources/hudson/PluginManager/check.jelly @@ -26,7 +26,7 @@ THE SOFTWARE. ${%lastUpdated(app.updateCenter.lastUpdatedString)} - + ${%Check now}
diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates.jelly b/core/src/main/resources/hudson/PluginManager/checkUpdates.jelly deleted file mode 100644 index 901293af95..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates.jelly +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - -
-

${%Checking Updates...}

- ${%Go back to update center} -
- -
-
-
diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_bg.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_bg.properties deleted file mode 100644 index 2e06417a0b..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_bg.properties +++ /dev/null @@ -1,30 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2015, 2016, Alexander Shopov -# -# 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. - -Go\ back\ to\ update\ center=\ - \u041a\u044a\u043c \u0441\u0430\u0439\u0442\u0430 \u0437\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f -Update\ Center=\ - \u0421\u0430\u0439\u0442 \u0437\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f -Done=\ - \u0413\u043e\u0442\u043e\u0432\u043e -Checking\ Updates...=\ - \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0437\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f\u2026 diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_da.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_da.properties deleted file mode 100644 index 34d3267d4e..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_da.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. Kohsuke Kawaguchi. Knud Poulsen. -# -# 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. - -Done=Udf\u00f8rt -Go\ back\ to\ update\ center=G\u00e5 tilbage til update center -Checking\ Updates...=Checker for opdateringer... diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_de.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_de.properties deleted file mode 100644 index aae8721d6a..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_de.properties +++ /dev/null @@ -1,4 +0,0 @@ -Checking\ Updates...=Suche nach Updates... -Go\ back\ to\ update\ center=Zurück zum Update-Center -Done=Fertig -Update\ Center=Update-Center diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_es.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_es.properties deleted file mode 100644 index e96d6134bc..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_es.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, 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. - -Checking\ Updates...=Buscando actualizaciones ... -Go\ back\ to\ update\ center=Volver al centro de actualizaciones. -Done=Terminado -Update\ Center=Centro de actualizaciones diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_fi.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_fi.properties deleted file mode 100644 index ddf3c99854..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_fi.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, 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. - -Checking\ Updates...=Tarkastetaan p\u00E4ivityksi\u00E4... -Done=Valmis -Go\ back\ to\ update\ center=Palaa takaisin liit\u00E4nn\u00E4isten hallintaan diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_fr.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_fr.properties deleted file mode 100644 index 00150f5638..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_fr.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Eric Lefevre-Ardant -# -# 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. - -Checking\ Updates...=Recherche des mises à jour... -Go\ back\ to\ update\ center=Retourner à la page principale des mises à jour -Done=Terminé diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_it.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_it.properties deleted file mode 100644 index 9bd80d3b77..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_it.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributors -# -# 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. - -Checking\ Updates...=Controllo aggiornamenti in corso... -Update\ Center=Centro aggiornamenti -Done=Fatto -Go\ back\ to\ update\ center=Torna al Centro aggiornamenti diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_ja.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_ja.properties deleted file mode 100644 index 214f2da271..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_ja.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe -# -# 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. - -Checking\ Updates...=\u66f4\u65b0\u3092\u30c1\u30a7\u30c3\u30af\u4e2d... -Go\ back\ to\ update\ center=\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30bb\u30f3\u30bf\u30fc\u3078\u623b\u308b -Done=\u5b8c\u4e86 -Update\ Center=\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30bb\u30f3\u30bf\u30fc diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_lv.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_lv.properties deleted file mode 100644 index 77dc0089ea..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_lv.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, 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. - -Checking\ Updates...=P\u0101rbauda atjaunin\u0101jumus -Done=Izpild\u012Bts -Go\ back\ to\ update\ center=Atgriezties Atjaunin\u0101jumu Centr\u0101 diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_nl.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_nl.properties deleted file mode 100644 index 6a68aa3c9c..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_nl.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Wim Rosseel -# -# 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. - -Checking\ Updates...=Zoekt actuele versies... -Go\ back\ to\ update\ center=Ga terug naar het actualisatiecentrum -Done=Klaar diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_pt_BR.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_pt_BR.properties deleted file mode 100644 index bd522482db..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_pt_BR.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc., Cleiber Silva -# -# 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. - -Checking\ Updates...=Procurando atualiza\u00e7\u00f5es... -Done=Feito -Go\ back\ to\ update\ center=Voltar \u00e0 central de atualiza\u00e7\u00f5es -Update\ Center=Central de atualiza\u00e7\u00f5es diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_pt_PT.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_pt_PT.properties deleted file mode 100644 index 555771d03c..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_pt_PT.properties +++ /dev/null @@ -1,5 +0,0 @@ -# This file is under the MIT License by authors - -Checking\ Updates...=Verificando por Atualiza\u00E7\u00F5es... -Done=Finalizado -Go\ back\ to\ update\ center=Voltar ao centro de atualiza\u00E7\u00E3o diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_ru.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_ru.properties deleted file mode 100644 index 6cee8c2dea..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_ru.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, 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. - -Checking\ Updates...=\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439... -Done=\u041E\u043A\u043E\u043D\u0447\u0435\u043D\u0430 -Go\ back\ to\ update\ center=\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F \u0432 \u0446\u0435\u043D\u0442\u0440 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439 diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_sk.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_sk.properties deleted file mode 100644 index 63be8b2c77..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_sk.properties +++ /dev/null @@ -1,5 +0,0 @@ -# This file is under the MIT License by authors - -Checking\ Updates...=Kontrolujem aktualiz\u00E1cie... -Done=Hotovo -Go\ back\ to\ update\ center=Sp\u00E4\u0165 na str\u00E1nku aktualiz\u00E1ci\u00ED diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_sr.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_sr.properties deleted file mode 100644 index c970a3513f..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_sr.properties +++ /dev/null @@ -1,6 +0,0 @@ -# This file is under the MIT License by authors - -Update\ Center=\u0426\u0435\u043D\u0442\u0430\u0440 \u0437\u0430 \u0410\u0436\u0443\u0440\u0438\u0440\u0430\u045A\u0435 -Checking\ Updates...=\u041F\u0440\u043E\u0432\u0435\u0440\u0438 \u0430\u0436\u0443\u0440\u0438\u0440\u0430\u045A\u0430 -Done=\u0413\u043E\u0442\u043E\u0432\u043E -Go\ back\ to\ update\ center=\u041D\u0430\u0437\u0430\u0434 \u043A\u0430 \u0446\u0435\u043D\u0442\u0440\u0443 \u0437\u0430 \u0430\u0436\u0443\u0440\u0438\u0440\u0430\u045A\u0435 diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_sv_SE.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_sv_SE.properties deleted file mode 100644 index a4bb977524..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_sv_SE.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, 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. - -Checking\ Updates...=Letar efter uppdateringar... -Done=Avslutat diff --git a/core/src/main/resources/hudson/PluginManager/checkUpdates_zh_TW.properties b/core/src/main/resources/hudson/PluginManager/checkUpdates_zh_TW.properties deleted file mode 100644 index 91aa340d18..0000000000 --- a/core/src/main/resources/hudson/PluginManager/checkUpdates_zh_TW.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2013, Sun Microsystems, Inc., Chunghwa Telecom Co., Ltd., -# and Pei-Tang Huang -# -# 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. - -Update\ Center=\u66f4\u65b0\u4e2d\u5fc3 -Checking\ Updates...=\u6b63\u5728\u6aa2\u67e5\u66f4\u65b0... -Go\ back\ to\ update\ center=\u56de\u5230\u66f4\u65b0\u4e2d\u5fc3 -Done=\u5b8c\u6210 diff --git a/core/src/main/resources/hudson/PluginManager/index.jelly b/core/src/main/resources/hudson/PluginManager/index.jelly index bfa80c21fc..27970a0709 100644 --- a/core/src/main/resources/hudson/PluginManager/index.jelly +++ b/core/src/main/resources/hudson/PluginManager/index.jelly @@ -29,9 +29,11 @@ THE SOFTWARE.
+ ${%Select}: ${%All}, ${%Compatible}, ${%None}
+
${%UpdatePageDescription}
${%UpdatePageLegend(rootURL+'/updateCenter/')} diff --git a/core/src/main/resources/hudson/PluginManager/installed.jelly b/core/src/main/resources/hudson/PluginManager/installed.jelly index b934172496..6291241e99 100644 --- a/core/src/main/resources/hudson/PluginManager/installed.jelly +++ b/core/src/main/resources/hudson/PluginManager/installed.jelly @@ -27,14 +27,32 @@ THE SOFTWARE. --> - + + + + + + + + + -
- ${%Filter}: - +
+ +
${%Warning}: ${%requires.restart}
@@ -66,15 +84,18 @@ THE SOFTWARE. ${%Enabled} ${%Name} ${%Version} - ${%Previously installed version} - ${%Uninstall} + + ${%Previously installed version} + ${%Uninstall} + - + @@ -102,71 +123,93 @@ THE SOFTWARE.
+ +
${%securityWarning} + +
+
${%deprecationWarning}
+ +
+ ${%adoptThisPlugin} +
+
${p.version} - - -
- - -
- - - - - - - - - - + + + +
+ + +
+ - -

${%Uninstallation pending}

-
- - -
- - - -
-
- -
- - - -
-
-
- -
-
+ + + + + +
- + + + +

${%Uninstallation pending}

+
+ + +
+ + + +
+
+ +
+ + + +
+
+
+ +
+
+
+ +
- + - + + + -

Failed : ${p.name}

-
-
${p.exceptionString}
+
+ ${p.name} +
+
+
${p.cause.message}
+ + diff --git a/core/src/main/resources/hudson/PluginManager/installed.properties b/core/src/main/resources/hudson/PluginManager/installed.properties index f318690a5c..5905200350 100644 --- a/core/src/main/resources/hudson/PluginManager/installed.properties +++ b/core/src/main/resources/hudson/PluginManager/installed.properties @@ -28,3 +28,8 @@ 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 +securityWarning=\ + Warning: The currently installed plugin version may not be safe to use. Please review the following security notices: +adoptThisPlugin=\ + This plugin is up for adoption! We are looking for new maintainers. \ + Visit our Adopt a Plugin initiative for more information. diff --git a/core/src/main/resources/hudson/PluginManager/sidepanel.groovy b/core/src/main/resources/hudson/PluginManager/sidepanel.groovy index a3aa54878d..94e396f731 100644 --- a/core/src/main/resources/hudson/PluginManager/sidepanel.groovy +++ b/core/src/main/resources/hudson/PluginManager/sidepanel.groovy @@ -28,7 +28,7 @@ l.header() l.side_panel { l.tasks { l.task(icon:"icon-up icon-md", href:rootURL+'/', title:_("Back to Dashboard")) - l.task(icon:"icon-gear2 icon-md", href:"${rootURL}/manage", title:_("Manage Jenkins"), permission:app.MANAGE, it:app) + l.task(icon:"icon-gear2 icon-md", href:"${rootURL}/manage", title:_("Manage Jenkins")) if (!app.updateCenter.jobs.isEmpty()) { l.task(icon:"icon-plugin icon-md", href:"${rootURL}/updateCenter/", title:_("Update Center")) } diff --git a/core/src/main/resources/hudson/PluginManager/tabBar.jelly b/core/src/main/resources/hudson/PluginManager/tabBar.jelly index f29d556105..8109281b4a 100644 --- a/core/src/main/resources/hudson/PluginManager/tabBar.jelly +++ b/core/src/main/resources/hudson/PluginManager/tabBar.jelly @@ -31,8 +31,6 @@ THE SOFTWARE. - - diff --git a/core/src/main/resources/hudson/PluginManager/table.jelly b/core/src/main/resources/hudson/PluginManager/table.jelly index 1d4423eb7d..aa63130e4e 100644 --- a/core/src/main/resources/hudson/PluginManager/table.jelly +++ b/core/src/main/resources/hudson/PluginManager/table.jelly @@ -22,37 +22,38 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> - - - - - - + + + + + Variable to contain List of Plugin + + + Page name to be passed to local:tabBar; 'updates' or 'available'. + + + If true, will hide all rows by default, and only the filter field will show them. + + + - +
- ${%Filter}: - +
@@ -61,43 +62,55 @@ THE SOFTWARE.
- - - + + + + + + - + + + + + - - - - - - - - - - - - + + + + + - diff --git a/core/src/main/resources/lib/form/repeatable.jelly b/core/src/main/resources/lib/form/repeatable.jelly index 09098d9203..06f3dbaad3 100644 --- a/core/src/main/resources/lib/form/repeatable.jelly +++ b/core/src/main/resources/lib/form/repeatable.jelly @@ -132,7 +132,7 @@ THE SOFTWARE.
-
${header}
+
${header}
@@ -145,7 +145,7 @@ THE SOFTWARE.
-
${header}
+
${header}
@@ -155,7 +155,7 @@ THE SOFTWARE.
-
${header}
+
${header}
diff --git a/core/src/main/resources/lib/form/slave-mode.jelly b/core/src/main/resources/lib/form/slave-mode.jelly index 61c8edc299..515fd7e7ca 100644 --- a/core/src/main/resources/lib/form/slave-mode.jelly +++ b/core/src/main/resources/lib/form/slave-mode.jelly @@ -36,10 +36,19 @@ THE SOFTWARE. - + + + ${node.mode} + + + + + + + \ No newline at end of file diff --git a/core/src/main/resources/lib/form/submit.jelly b/core/src/main/resources/lib/form/submit.jelly index 9eb6324917..b66898ecee 100644 --- a/core/src/main/resources/lib/form/submit.jelly +++ b/core/src/main/resources/lib/form/submit.jelly @@ -36,6 +36,14 @@ THE SOFTWARE. The text of the submit button. Something like "submit", "OK", etc. + + + Set to "true" to enable the large variant of the button + - + + diff --git a/core/src/main/resources/lib/form/textarea.jelly b/core/src/main/resources/lib/form/textarea.jelly index 08c86f4333..adf84966ed 100644 --- a/core/src/main/resources/lib/form/textarea.jelly +++ b/core/src/main/resources/lib/form/textarea.jelly @@ -86,22 +86,24 @@ THE SOFTWARE. org.kohsuke.stapler.codemirror.theme.default"/> - - - ${customizedFields.add(name)} - - -
+ + + + ${customizedFields.add(name)} + + +
+
diff --git a/core/src/main/resources/lib/form/textbox.jelly b/core/src/main/resources/lib/form/textbox.jelly index 0f6cf0ea21..ad40892bd7 100644 --- a/core/src/main/resources/lib/form/textbox.jelly +++ b/core/src/main/resources/lib/form/textbox.jelly @@ -65,7 +65,7 @@ THE SOFTWARE. which is the recommended approach. - Specify 'post' (must be lowercase) to change the HTTP method used for the AJAX requests to @checkUrl from a GET to a POST. + Specify 'post' (must be lowercase) to change the HTTP method used for the AJAX requests to @checkUrl from a GET to a POST. If any other value is specified then requests will use GET. @@ -82,12 +82,15 @@ THE SOFTWARE. - + + + + ${customizedFields.add(name)} diff --git a/core/src/main/resources/lib/hudson/buildCaption.jelly b/core/src/main/resources/lib/hudson/buildCaption.jelly index ed5d0ea987..4193d4a169 100644 --- a/core/src/main/resources/lib/hudson/buildCaption.jelly +++ b/core/src/main/resources/lib/hudson/buildCaption.jelly @@ -24,7 +24,6 @@ THE SOFTWARE. @@ -47,7 +46,7 @@ THE SOFTWARE. diff --git a/core/src/main/resources/lib/hudson/editableDescription.jelly b/core/src/main/resources/lib/hudson/editableDescription.jelly index b47a03c9bd..585b844c2b 100644 --- a/core/src/main/resources/lib/hudson/editableDescription.jelly +++ b/core/src/main/resources/lib/hudson/editableDescription.jelly @@ -40,7 +40,7 @@ THE SOFTWARE.
-
+
diff --git a/core/src/main/resources/lib/hudson/executors.jelly b/core/src/main/resources/lib/hudson/executors.jelly index 663d7b64e2..4cd01bcadd 100644 --- a/core/src/main/resources/lib/hudson/executors.jelly +++ b/core/src/main/resources/lib/hudson/executors.jelly @@ -155,7 +155,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/lib/hudson/help.jelly b/core/src/main/resources/lib/hudson/help.jelly index 3f3230f69f..faef6068b9 100644 --- a/core/src/main/resources/lib/hudson/help.jelly +++ b/core/src/main/resources/lib/hudson/help.jelly @@ -21,11 +21,22 @@ 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. --> - - - - - + + + Icon linked to the specified @href + + + Destination of the help icon's link + + + The tooltip to display + + + Icon size, available are: small, medium, large, xlarge + + + + + + diff --git a/core/src/main/resources/lib/hudson/queue.jelly b/core/src/main/resources/lib/hudson/queue.jelly index 914657dad6..776c4ed46a 100644 --- a/core/src/main/resources/lib/hudson/queue.jelly +++ b/core/src/main/resources/lib/hudson/queue.jelly @@ -51,7 +51,7 @@ THE SOFTWARE.
@@ -101,7 +101,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/lib/hudson/scriptConsole.jelly b/core/src/main/resources/lib/hudson/scriptConsole.jelly index a0d24b32e0..9cb0503151 100644 --- a/core/src/main/resources/lib/hudson/scriptConsole.jelly +++ b/core/src/main/resources/lib/hudson/scriptConsole.jelly @@ -27,7 +27,7 @@ THE SOFTWARE. --> - + diff --git a/core/src/main/resources/lib/layout/breadcrumbBar.jelly b/core/src/main/resources/lib/layout/breadcrumbBar.jelly index ef1b411328..74a8b1755a 100644 --- a/core/src/main/resources/lib/layout/breadcrumbBar.jelly +++ b/core/src/main/resources/lib/layout/breadcrumbBar.jelly @@ -39,49 +39,35 @@ THE SOFTWARE.
-
- -
- - + + + + + + + + - - -
diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_ar.properties b/core/src/main/resources/lib/layout/breadcrumbBar_ar.properties deleted file mode 100644 index 0b2e50fa90..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_ar.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -ENABLE\ AUTO\ REFRESH=\u062A\u0645\u0643\u064A\u0646 \u0627\u0644\u062A\u062D\u062F\u064A\u062B \u0627\u0644\u062A\u0644\u0642\u0627\u0626\u064A diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_bg.properties b/core/src/main/resources/lib/layout/breadcrumbBar_bg.properties deleted file mode 100644 index 0196e2201b..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_bg.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2015, 2016, Alexander Shopov -# -# 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. - -DISABLE\ AUTO\ REFRESH=\ - \u0411\u0435\u0437 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043f\u0440\u0435\u0437\u0430\u0440\u0435\u0436\u0434\u0430\u043d\u0435 -ENABLE\ AUTO\ REFRESH=\ - \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043f\u0440\u0435\u0437\u0430\u0440\u0435\u0436\u0434\u0430\u043d\u0435 diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_ca.properties b/core/src/main/resources/lib/layout/breadcrumbBar_ca.properties deleted file mode 100644 index c352446bce..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_ca.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=DESHABILITAR REFRESC AUTOM\u00C0TIC -ENABLE\ AUTO\ REFRESH=HABILITAR REFRESC AUTOM\u00C0TIC diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_cs.properties b/core/src/main/resources/lib/layout/breadcrumbBar_cs.properties deleted file mode 100644 index 69854bf8cc..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_cs.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=Vypnout automatick\u00E9 obnovov\u00E1n\u00ED str\u00E1nky -ENABLE\ AUTO\ REFRESH=POVOLIT AUTO REFRESH diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_da.properties b/core/src/main/resources/lib/layout/breadcrumbBar_da.properties deleted file mode 100644 index 7ee5df8a97..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_da.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=Autoopdater ikke siden -ENABLE\ AUTO\ REFRESH=automatisk genopfriskning diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_de.properties b/core/src/main/resources/lib/layout/breadcrumbBar_de.properties deleted file mode 100644 index d497257401..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_de.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers -# -# 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. - -DISABLE\ AUTO\ REFRESH=AUTO-AKTUALISIERUNG AUSSCHALTEN -ENABLE\ AUTO\ REFRESH=AUTO-AKTUALISIERUNG EINSCHALTEN diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_el.properties b/core/src/main/resources/lib/layout/breadcrumbBar_el.properties deleted file mode 100644 index 4c0bfff064..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_el.properties +++ /dev/null @@ -1,5 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=\u0386\u03C0\u03B5\u03BD\u03B5\u03C1\u03B3\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 \u03B1\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7\u03C2 \u03B1\u03BD\u03B1\u03BD\u03AD\u03C9\u03C3\u03B7\u03C2 - -ENABLE\ AUTO\ REFRESH=\u0395\u03BD\u03B5\u03C1\u03B3\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 \u03B1\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7\u03C2 \u03B1\u03BD\u03B1\u03BD\u03AD\u03C9\u03C3\u03B7\u03C2 diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_es.properties b/core/src/main/resources/lib/layout/breadcrumbBar_es.properties deleted file mode 100644 index 8eb26d5e7a..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_es.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers -# -# 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. - -DISABLE\ AUTO\ REFRESH=DESHABILITAR REFRESCO AUTOM\u00C1TICO - -ENABLE\ AUTO\ REFRESH=ACTIVAR AUTO REFRESCO diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_es_AR.properties b/core/src/main/resources/lib/layout/breadcrumbBar_es_AR.properties deleted file mode 100644 index 8688f104ea..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_es_AR.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -ENABLE\ AUTO\ REFRESH=Habilitar Auto Refresco diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_et.properties b/core/src/main/resources/lib/layout/breadcrumbBar_et.properties deleted file mode 100644 index 5306a3fb64..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_et.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -ENABLE\ AUTO\ REFRESH=LUBA AUTOMAATNE V\u00C4RSKENDAMINE -DISABLE\ AUTO\ REFRESH=KEELA AUTOMAATNE V\u00C4RSKENDAMINE diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_fi.properties b/core/src/main/resources/lib/layout/breadcrumbBar_fi.properties deleted file mode 100644 index 602136e5f8..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_fi.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=POISTA AUTOMAATTINEN P\u00C4IVITYS K\u00C4YT\u00D6ST\u00C4 -ENABLE\ AUTO\ REFRESH=Salli n\u00E4kym\u00E4n virkist\u00E4minen diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_fr.properties b/core/src/main/resources/lib/layout/breadcrumbBar_fr.properties deleted file mode 100644 index aa6da31c8a..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_fr.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers -# -# 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. - -DISABLE\ AUTO\ REFRESH=D\u00E9sactiver le rafra\u00EEchissement automatique - -ENABLE\ AUTO\ REFRESH=Rafra\u00EEchissement automatique diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_he.properties b/core/src/main/resources/lib/layout/breadcrumbBar_he.properties deleted file mode 100644 index 37fb1041cd..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_he.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -ENABLE\ AUTO\ REFRESH=\u05D0\u05E4\u05E9\u05E8 \u05E8\u05E2\u05E0\u05D5\u05DF \u05D0\u05D5\u05D8\u05D5\u05DE\u05D8\u05D9 diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_hu.properties b/core/src/main/resources/lib/layout/breadcrumbBar_hu.properties deleted file mode 100644 index a377c71f13..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_hu.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=Automatikus oldalfriss\u00EDt\u00E9s letilt\u00E1sa -ENABLE\ AUTO\ REFRESH=AUTOMATIKUS FRISS\u00CDT\u00C9S ENGED\u00C9LYEZ\u00C9SE diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_it.properties b/core/src/main/resources/lib/layout/breadcrumbBar_it.properties deleted file mode 100644 index f8fe07c89a..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_it.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=DISABILITA AGGIORNAMENTO AUTOMATICO -ENABLE\ AUTO\ REFRESH=ABILITA AGGIORNAMENTO AUTOMATICO diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_ja.properties b/core/src/main/resources/lib/layout/breadcrumbBar_ja.properties deleted file mode 100644 index 3a49b6b244..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_ja.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2012, Seiji Sogabe -# -# 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. - -ENABLE\ AUTO\ REFRESH=\u81EA\u52D5\u30EA\u30ED\u30FC\u30C9\u3092on -DISABLE\ AUTO\ REFRESH=\u81EA\u52D5\u30EA\u30ED\u30FC\u30C9\u3092off diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_ko.properties b/core/src/main/resources/lib/layout/breadcrumbBar_ko.properties deleted file mode 100644 index ff69aa319d..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_ko.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=\uC790\uB3D9 \uC7AC\uC2DC\uC791 \uBE44 \uD65C\uC131\uD654 -ENABLE\ AUTO\ REFRESH=\uC790\uB3D9\uC73C\uB85C \uC0C8\uB85C\uACE0\uCE68 diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_lt.properties b/core/src/main/resources/lib/layout/breadcrumbBar_lt.properties deleted file mode 100644 index b326584542..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_lt.properties +++ /dev/null @@ -1,6 +0,0 @@ -# This file is under the MIT License by authors - -# /jenkins-core/src/main/resources/lib/layout/breadcrumbBar_lt.properties - -DISABLE\ AUTO\ REFRESH=I\u0160JUNGTI automatin\u012F puslapio atnaujinim\u0105 -ENABLE\ AUTO\ REFRESH=\u012EJUNGTI automatin\u012F puslapio atnaujinim\u0105 diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_lv.properties b/core/src/main/resources/lib/layout/breadcrumbBar_lv.properties deleted file mode 100644 index 18ee12ca92..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_lv.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=ATSP\u0112JOT AUTOM\u0100TISKO ATSVAIDZIN\u0100S\u0100NU -ENABLE\ AUTO\ REFRESH=Ieslegt automatisku atjauno\u0161anu diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_nb_NO.properties b/core/src/main/resources/lib/layout/breadcrumbBar_nb_NO.properties deleted file mode 100644 index 4f35e256a2..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_nb_NO.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=DEAKTIVER AUTOOPPDATERING -ENABLE\ AUTO\ REFRESH=Sl\u00E5 p\u00E5 auto oppdatering diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_nl.properties b/core/src/main/resources/lib/layout/breadcrumbBar_nl.properties deleted file mode 100644 index 752f5c0cfe..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_nl.properties +++ /dev/null @@ -1,5 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=ZET AUTOMATISCH VERNIEUWEN UIT - -ENABLE\ AUTO\ REFRESH=ZET AUTOMATISCH VERNIEUWEN AAN diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_pl.properties b/core/src/main/resources/lib/layout/breadcrumbBar_pl.properties deleted file mode 100644 index 1055d459e1..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_pl.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=WY\u0141\u0104CZ AUTO OD\u015AWIE\u017BANIE -ENABLE\ AUTO\ REFRESH=W\u0141\u0104CZ AUTOMATYCZNE OD\u015AWIE\u017BANIE diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_pt.properties b/core/src/main/resources/lib/layout/breadcrumbBar_pt.properties deleted file mode 100644 index e2df18bc05..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_pt.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers -# -# 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. - -ENABLE\ AUTO\ REFRESH=Ativar atualiza\u00E7\u00E3o autom\u00E1tica -DISABLE\ AUTO\ REFRESH=Desativar atualiza\u00E7\u00E3o autom\u00E1tica diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_pt_BR.properties b/core/src/main/resources/lib/layout/breadcrumbBar_pt_BR.properties deleted file mode 100644 index ad28910755..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_pt_BR.properties +++ /dev/null @@ -1,5 +0,0 @@ -# This file is under the MIT License by authors -# Translated by Fernando Boaglio - -DISABLE\ AUTO\ REFRESH=DESABILITAR ATUALIZA\u00c7\u00c3O AUTOM\u00c1TICA -ENABLE\ AUTO\ REFRESH=HABILITAR ATUALIZA\u00C7\u00C3O AUTOM\u00C1TICA diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_pt_PT.properties b/core/src/main/resources/lib/layout/breadcrumbBar_pt_PT.properties deleted file mode 100644 index cabe35349b..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_pt_PT.properties +++ /dev/null @@ -1,5 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=Desativar refrescamento autom\u00E1tico - -ENABLE\ AUTO\ REFRESH=ATIVAR AUTO-ATUALIZA\u00C7\u00C3O diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_ro.properties b/core/src/main/resources/lib/layout/breadcrumbBar_ro.properties deleted file mode 100644 index 7ae30945a0..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_ro.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=DEZACTIVEAZ\u0102 RE\u00CENC\u0102RCAREA AUTOMAT\u0102 -ENABLE\ AUTO\ REFRESH=ACTIVEAZA ACTUALIZAREA AUTOMATA diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_ru.properties b/core/src/main/resources/lib/layout/breadcrumbBar_ru.properties deleted file mode 100644 index f1946cea35..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_ru.properties +++ /dev/null @@ -1,6 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=\u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 - -ENABLE\ AUTO\ REFRESH=\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 - diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_sk.properties b/core/src/main/resources/lib/layout/breadcrumbBar_sk.properties deleted file mode 100644 index 3b879203b7..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_sk.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=VYPN\u00DA\u0164 AUTOMATICK\u00C9 OBNOVENIE -ENABLE\ AUTO\ REFRESH=POVOLI\u0164 AUTOMATICK\u00DA OBNOVU diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_sl.properties b/core/src/main/resources/lib/layout/breadcrumbBar_sl.properties deleted file mode 100644 index 504e408780..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_sl.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=ONEMOGO\u010CI SAMODEJNO OSVE\u017DEVANJE -ENABLE\ AUTO\ REFRESH=Onemogo\u010Di avtomatsko osve\u017Eevanje diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_sr.properties b/core/src/main/resources/lib/layout/breadcrumbBar_sr.properties deleted file mode 100644 index 335d0c0346..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_sr.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -ENABLE\ AUTO\ REFRESH=\u0421\u0430 \u0430\u0443\u0442\u043E\u043C\u0430\u0442\u0441\u043A\u0438\u043C \u043E\u0441\u0432\u0435\u0436\u0438\u0432\u0430\u045A\u0435\u043C -DISABLE\ AUTO\ REFRESH=\u0411\u0435\u0437 \u0430\u0443\u0442\u043E\u043C\u0430\u0442\u0441\u043A\u043E\u0433 \u043E\u0441\u0432\u0435\u0436\u0438\u0432\u0430\u045A\u0435\u043C diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_sv_SE.properties b/core/src/main/resources/lib/layout/breadcrumbBar_sv_SE.properties deleted file mode 100644 index 51df0def9e..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_sv_SE.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=Autouppdatera ej sidan -ENABLE\ AUTO\ REFRESH=AKTIVERA AUTOUPPDATERING diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_tr.properties b/core/src/main/resources/lib/layout/breadcrumbBar_tr.properties deleted file mode 100644 index 06661c79f6..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_tr.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=Otomatik Yenilemeyi Kapat -ENABLE\ AUTO\ REFRESH=Otomatik Yenilemeyi A\u00E7 diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_uk.properties b/core/src/main/resources/lib/layout/breadcrumbBar_uk.properties deleted file mode 100644 index 4af7832cf6..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_uk.properties +++ /dev/null @@ -1,5 +0,0 @@ -# This file is under the MIT License by authors - -DISABLE\ AUTO\ REFRESH=\u0412\u0418\u041A\u041B\u042E\u0427\u0418\u0422\u0418 \u0410\u0412\u0422\u041E\u041E\u041D\u041E\u0412\u041B\u0415\u041D\u041D\u042F - -ENABLE\ AUTO\ REFRESH=\u0423\u0432\u0456\u043C\u043A\u043D\u0443\u0442\u0438 \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u043D\u0435 \u043E\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u044F diff --git a/core/src/main/resources/lib/layout/breadcrumbBar_zh_TW.properties b/core/src/main/resources/lib/layout/breadcrumbBar_zh_TW.properties deleted file mode 100644 index ae26bf7e39..0000000000 --- a/core/src/main/resources/lib/layout/breadcrumbBar_zh_TW.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2013, Chunghwa Telecom Co., Ltd., Pei-Tang Huang -# -# 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. - -ENABLE\ AUTO\ REFRESH=\u958B\u555F\u81EA\u52D5\u66F4\u65B0\u9801\u9762 -DISABLE\ AUTO\ REFRESH=\u53D6\u6D88\u81EA\u52D5\u66F4\u65B0\u9801\u9762 diff --git a/core/src/main/resources/lib/layout/breadcrumbs.css b/core/src/main/resources/lib/layout/breadcrumbs.css index 26c4683a8a..b305e1c7c6 100644 --- a/core/src/main/resources/lib/layout/breadcrumbs.css +++ b/core/src/main/resources/lib/layout/breadcrumbs.css @@ -1,70 +1,98 @@ #left-top-nav { - text-align: left; - color: #222; -} - -#right-top-nav { - float: right; - margin-right: 1em; - height: 2em; - line-height: 2em; + text-align: left; + color: #222; } #left-top-nav a { - color: black; + color: black; } #top-nav .a { - color: white; + color: white; +} + +.breadcrumbs__wrapper { + display: flex; + justify-content: space-between; + align-items: center; + min-height: 2.5rem; + font-size: 1rem; + line-height: 1.5; + padding: 0 1.25rem; + background-color: #F8F8F8; } #breadcrumbs { list-style-type: none; - padding: 0 0 0 8px; margin: 0; - height: 2em; - border-bottom: 1px solid #d3d7cf; - display:block; + padding: 0; + min-height: 2.5rem; + display: block; + display: flex; + align-items: center; + flex: 1; + border-bottom: none; + flex-wrap: wrap; } #breadcrumbs LI { - float:left; - line-height:2em; - height: 2em; - color:#555753; display:inline-block; + min-height: 2.5rem; } #breadcrumbs LI A { - display: block; - padding-left: 0.75em; - line-height: 2em; + display: inline-block; + display: inline-flex; + align-items: center; + height: 2.5rem; + padding: 0 0.5rem; } #breadcrumbs LI:hover, A.breadcrumbBarAnchor.mouseIsOverMenuSelector { background-color: #e0e4dc; } -#breadcrumbs LI A:link, #breadcrumbs LI A:visited { - text-decoration:none; - color:#555753; - display: block; + +#breadcrumbs LI A, #breadcrumbs LI A:link, #breadcrumbs LI A:visited { + /* is this still needed? */ max-width: 1330px; - float: left; + text-overflow: ellipsis; + text-decoration: none; overflow: hidden; white-space: nowrap; + font-weight: bold; + + display: inline-block; + display: inline-flex; + color: #4d545d; } -#breadcrumbs LI A:hover, #crumbs LI A:focus { - color:#3465a4; +#breadcrumbs LI A:hover, #breadcrumbs LI A:focus { + /* TODO: change hover state + add it in the LI element so that it applies to the menuSeparator */ + text-decoration: underline; + outline-color: #3FB3F7; +} + +#breadcrumbs LI A:active { + background-color: #C8CEC2; +} + +#breadcrumbs LI A { + cursor: pointer; } #breadcrumbs LI.children, #breadcrumbs LI.separator {/* '>' separator between two items */ - width: 16px; + width: 1.25rem; + height: 100%; + display: inline-block; + display: inline-flex; + align-items: center; background-image: url(menu_right_arrow2.png); background-position: center center; background-repeat: no-repeat; + } #breadcrumbs LI.children { @@ -80,21 +108,26 @@ } #menuSelector {/* used for showing 'v' on the right of the anchor */ - background-color:transparent; - background-image: url(menu_down_arrow.png); - background-position: center center; - background-repeat: no-repeat; - width: 15px; - height:16px; - position: absolute; - visibility: hidden; - cursor: pointer; - z-index: 2000; + background-color:transparent; + background-image: url(menu_down_arrow.png); + background-position: center center; + background-repeat: no-repeat; + width: 15px; + height:16px; + position: absolute; + visibility: hidden; + cursor: pointer; + z-index: 2000; } #menuSelector.inverse { - background-image: url(menu_down_arrow2.png); + background-image: url(menu_down_arrow2.png); } A.model-link.inside, #breadcrumbs A.inside {/* additional 'inside' class allows pre-allocation of the context menu space */ - padding-right: 16px; + padding-right: 16px; +} + +#breadcrumb-menu .header { + font-weight: bold; + font-size: 0.875rem; } diff --git a/core/src/main/resources/lib/layout/breadcrumbs.js b/core/src/main/resources/lib/layout/breadcrumbs.js index adbebc8435..de91518b11 100644 --- a/core/src/main/resources/lib/layout/breadcrumbs.js +++ b/core/src/main/resources/lib/layout/breadcrumbs.js @@ -28,7 +28,7 @@ var breadcrumbs = (function() { } Event.observe(window,"load",function(){ - menu = new YAHOO.widget.Menu("breadcrumb-menu", {position:"dynamic", hidedelay:1000, zIndex:2001}); + menu = new YAHOO.widget.Menu("breadcrumb-menu", {position:"dynamic", hidedelay:1000, zIndex:2001, scrollincrement: 2}); }); @@ -203,7 +203,11 @@ var breadcrumbs = (function() { onComplete:function (x) { var a = x.responseText.evalJSON().items; function fillMenuItem(e) { - e.text = makeMenuHtml(e.icon, e.displayName); + if (e.header) { + e.text = makeMenuHtml(e.icon, "" + e.displayName + ""); + } else { + e.text = makeMenuHtml(e.icon, e.displayName); + } if (e.subMenu!=null) e.subMenu = {id:"submenu"+(iota++), itemdata:e.subMenu.items.each(fillMenuItem)}; if (e.requiresConfirmation) { diff --git a/core/src/main/resources/lib/layout/copyButton/copyButton.css b/core/src/main/resources/lib/layout/copyButton/copyButton.css index 3dfb91706d..04c35214d1 100644 --- a/core/src/main/resources/lib/layout/copyButton/copyButton.css +++ b/core/src/main/resources/lib/layout/copyButton/copyButton.css @@ -9,8 +9,10 @@ background-repeat: no-repeat; width: 20px; height: 20px; + min-height: 0; + min-width: 0; padding: 0; - border: none; + border: none; } #jenkins .copy-button button:hover, #jenkins .copy-button button:active { diff --git a/core/src/main/resources/lib/layout/hasAdministerOrManage.jelly b/core/src/main/resources/lib/layout/hasAdministerOrManage.jelly new file mode 100644 index 0000000000..faf8f95da5 --- /dev/null +++ b/core/src/main/resources/lib/layout/hasAdministerOrManage.jelly @@ -0,0 +1,6 @@ + + + + + + diff --git a/core/src/main/resources/lib/layout/helpIcon.jelly b/core/src/main/resources/lib/layout/helpIcon.jelly new file mode 100644 index 0000000000..c7fe58c2fe --- /dev/null +++ b/core/src/main/resources/lib/layout/helpIcon.jelly @@ -0,0 +1,25 @@ + + + + Outputs a help icon + + The help link is rendered as an SVG with an (?) icon. + + @since TODO + + Icon size, available are: small, medium, large, xlarge + + + The tooltip to display + + + Class name to apply + + + + + + + diff --git a/core/src/main/resources/lib/layout/html.jelly b/core/src/main/resources/lib/layout/html.jelly index 0a56c939ad..52477b187c 100644 --- a/core/src/main/resources/lib/layout/html.jelly +++ b/core/src/main/resources/lib/layout/html.jelly @@ -37,8 +37,7 @@ THE SOFTWARE. Title of the HTML page. Rendered into <title> tag. - If non-null and not "false", auto refresh is disabled on this page. - This is necessary for pages that include forms. + Deprecated: Used to disable auto refresh for a page. The feature has been removed. specify path that starts from "/" for loading additional CSS stylesheet. @@ -69,7 +68,6 @@ THE SOFTWARE. --> - ${h.advertiseHeaders(response)} @@ -80,8 +78,8 @@ THE SOFTWARE.
- - + + ${h.checkPermission(it,permission)} diff --git a/core/src/main/resources/lib/layout/layout.jelly b/core/src/main/resources/lib/layout/layout.jelly index cac4b0d893..cc0ecedad5 100644 --- a/core/src/main/resources/lib/layout/layout.jelly +++ b/core/src/main/resources/lib/layout/layout.jelly @@ -34,8 +34,7 @@ THE SOFTWARE. Title of the HTML page. Rendered into <title> tag. - If non-null and not "false", auto refresh is disabled on this page. - This is necessary for pages that include forms. + Deprecated: Used to disable auto refresh for a page. The feature has been removed. specify path that starts from "/" for loading additional CSS stylesheet. @@ -49,12 +48,20 @@ THE SOFTWARE. in <l:header/> tag. - If given, this page is only made available to users that has the specified permission. + If given, this page is only made available to users who have the specified permission. (The permission will be checked against the "it" object.) + + If given, this page is made available to users who have one or more of the specified permissions. + (The permissions will be checked against the "it" object.) + Available values: two-column (by default), one-column (full-width size) or full-screen (since 2.53). + + Do not include Bootstrap 3 grid. When a plugin wants to use a more recent version of Bootstrap then + the bundled grid will cause conflicts. + @@ -81,7 +88,6 @@ THE SOFTWARE. - ${h.advertiseHeaders(response)} @@ -92,19 +98,22 @@ THE SOFTWARE. - - + + ${h.checkPermission(it,permission)} + ${h.checkAnyPermission(it, permissions)} ${h.appendIfNotNull(title, ' [Jenkins]', 'Jenkins')} - - + + - + + + @@ -150,12 +159,12 @@ THE SOFTWARE. - - + + @@ -185,60 +194,12 @@ THE SOFTWARE.
${%Install}${%Name}${%Version}${%Install}${%Name}${%Version}${%Released} ${%Installed}
+ ${%loading} +
${thisCat}
- -
+ +
+ + +
@@ -119,6 +132,9 @@ THE SOFTWARE.
${%javaWarning(p.minimumJavaVersion)}
+ +
${%Applying this update will address security vulnerabilities in the currently installed version.}
+
${%depCompatWarning}
${%parentDepCompatWarning} @@ -144,8 +160,20 @@ THE SOFTWARE.
+ +
+ ${%adoptThisPlugin} +
+
+ + + + @@ -162,7 +190,7 @@ THE SOFTWARE.
+
${%No updates}
@@ -175,13 +203,15 @@ THE SOFTWARE.
- - - - + + + + + + + - - +
diff --git a/core/src/main/resources/hudson/PluginManager/table.properties b/core/src/main/resources/hudson/PluginManager/table.properties index e2297fdde1..d9fe7d043f 100644 --- a/core/src/main/resources/hudson/PluginManager/table.properties +++ b/core/src/main/resources/hudson/PluginManager/table.properties @@ -20,7 +20,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. compatWarning=\ - Warning: the new version of this plugin claims to use a different settings format than the installed version. \ + Warning: The new version of this plugin is marked as incompatible with the installed version. \ + This is usually the case because its behavior changed, or because it uses a different settings format than the installed version. \ Jobs using this plugin may need to be reconfigured, and/or you may not be able to cleanly revert to the prior version without manually restoring old settings. \ Consult the plugin release notes for details. parentDepCompatWarning=The following plugins are incompatible: @@ -32,17 +33,24 @@ javaWarning=\ Warning: This plugin requires Java {0} or newer. \ Jenkins will refuse to load this plugin if installed. depCompatWarning=\ - Warning: This plugin requires dependent plugins be upgraded and at least one of these dependent plugins claims to use a different settings format than the installed version. \ + Warning: This plugin requires newer versions of dependencies and at least one of those plugins is not compatible with the installed version. \ + This is usually the case because its behavior changed, or it uses a different settings format than the installed version. \ Jobs using that plugin may need to be reconfigured, and/or you may not be able to cleanly revert to the prior version without manually restoring old settings. \ Consult the plugin release notes for details. depCoreWarning=\ - Warning: This plugin requires dependent plugins that require Jenkins {0} or newer. \ - Jenkins will refuse to load the dependent plugins requiring a newer version of Jenkins, \ + Warning: This plugin has dependencies on other plugins that require Jenkins {0} or newer. \ + Jenkins will refuse to load the dependencies requiring a newer version of Jenkins, \ and in turn loading this plugin will fail. depJavaWarning=\ - Warning: this plugin requires other plugins which in turn require Java {0} or newer. \ - Jenkins will refuse to load the dependent plugins requiring a newer version of Jenkins, \ + Warning: this plugin has dependencies on other plugins that require Java {0} or newer. \ + Jenkins will refuse to load the dependencies requiring a newer version of Jenkins, \ and in turn loading this plugin will fail. securityWarning=\ Warning: This plugin version may not be safe to use. Please review the following security notices: +ago={0} ago +adoptThisPlugin=\ + This plugin is up for adoption! We are looking for new maintainers. \ + Visit our Adopt a Plugin initiative for more information. +loading=Plugin list is loading... +instructions=Use the search field above to search for available plugins. diff --git a/core/src/main/resources/hudson/PluginWrapper/thirdPartyLicenses.jelly b/core/src/main/resources/hudson/PluginWrapper/thirdPartyLicenses.jelly index acdbbb6b9e..76d64570d5 100644 --- a/core/src/main/resources/hudson/PluginWrapper/thirdPartyLicenses.jelly +++ b/core/src/main/resources/hudson/PluginWrapper/thirdPartyLicenses.jelly @@ -25,7 +25,7 @@ THE SOFTWARE. - +

${%about(it.longName+' '+it.version)}

diff --git a/core/src/main/resources/hudson/ProxyConfiguration/config.groovy b/core/src/main/resources/hudson/ProxyConfiguration/config.groovy index 0be2a539b6..2ca49389b9 100644 --- a/core/src/main/resources/hudson/ProxyConfiguration/config.groovy +++ b/core/src/main/resources/hudson/ProxyConfiguration/config.groovy @@ -1,6 +1,11 @@ package hudson.ProxyConfiguration def f=namespace(lib.FormTagLib) +def l=namespace(lib.LayoutTagLib) + +if (!h.hasPermission(app.ADMINISTER)) { + set("readOnlyMode", "true") +} f.entry(title:_("Server"),field:"name") { f.textbox() @@ -17,10 +22,13 @@ f.entry(title:_("Password"),field:"secretPassword") { f.entry(title:_("No Proxy Host"),field:"noProxyHost") { f.textarea() } -f.advanced(){ - f.entry(title:_("Test URL"),field:"testUrl") { - f.textbox() + +l.isAdmin() { + f.advanced() { + f.entry(title: _("Test URL"), field: "testUrl") { + f.textbox() + } + f.validateButton(title:_("Validate Proxy"), + method:"validateProxy", with:"testUrl,name,port,userName,secretPassword,noProxyHost") } - f.validateButton(title:_("Validate Proxy"), - method:"validateProxy", with:"testUrl,name,port,userName,password,noProxyHost") } diff --git a/core/src/main/resources/hudson/cli/CLIAction/command.jelly b/core/src/main/resources/hudson/cli/CLIAction/command.jelly index 02acaa65b4..53bfdeb078 100644 --- a/core/src/main/resources/hudson/cli/CLIAction/command.jelly +++ b/core/src/main/resources/hudson/cli/CLIAction/command.jelly @@ -24,7 +24,7 @@ THE SOFTWARE. - +

diff --git a/core/src/main/resources/hudson/cli/CLIAction/index.jelly b/core/src/main/resources/hudson/cli/CLIAction/index.jelly index 94ecddfb23..b7d671980e 100644 --- a/core/src/main/resources/hudson/cli/CLIAction/index.jelly +++ b/core/src/main/resources/hudson/cli/CLIAction/index.jelly @@ -24,7 +24,7 @@ THE SOFTWARE. - +

diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly index e6eecb28f2..c8b266c17e 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly @@ -30,7 +30,7 @@ THE SOFTWARE.

${%Manage Old Data}

${%blurb.1}

${%blurb.2}

- +
@@ -85,7 +85,7 @@ THE SOFTWARE.

${%Unreadable Data}

${%blurb.6}

-
${%Type}${%Name}${%Version}
+
diff --git a/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message.jelly b/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message.jelly index e2aee20eff..c86699d267 100644 --- a/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message.jelly +++ b/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message.jelly @@ -21,28 +21,18 @@ 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. --> - - - -
${%Type}${%Name}${%Error}
+ +
@@ -79,5 +57,5 @@ THE SOFTWARE.
${%Build}
-
+
diff --git a/core/src/main/resources/hudson/model/Job/buildTimeTrend_resources.css b/core/src/main/resources/hudson/model/Job/buildTimeTrend_resources.css new file mode 100644 index 0000000000..4cadb1d1b3 --- /dev/null +++ b/core/src/main/resources/hudson/model/Job/buildTimeTrend_resources.css @@ -0,0 +1,3 @@ +img.build-time-graph { + float: right; +} diff --git a/core/src/main/resources/hudson/model/Job/buildTimeTrend_resources.js b/core/src/main/resources/hudson/model/Job/buildTimeTrend_resources.js new file mode 100644 index 0000000000..b87cac2eb0 --- /dev/null +++ b/core/src/main/resources/hudson/model/Job/buildTimeTrend_resources.js @@ -0,0 +1,29 @@ +/** + * Public method to be called by progressiveRendering's callback + */ +function buildTimeTrend_displayBuilds(data) { + var p = document.getElementById('trend'); + var isMasterSlaveEnabled = 'true' === p.getAttribute("data-is-master-slave-enabled"); + var imagesURL = document.head.getAttribute('data-imagesurl'); + var rootURL = document.head.getAttribute('data-rooturl'); + + for (var x = 0; data.length > x; x++) { + var e = data[x]; + var tr = new Element('tr'); + tr.insert(new Element('td', {data: e.iconColorOrdinal}). + insert(new Element('a', {href: e.number + '/console'}). + insert(new Element('img', {width: 16, height: 16, src: imagesURL+ '/16x16/' + e.buildStatusUrl, alt: e.iconColorDescription})))); + tr.insert(new Element('td', {data: e.number}). + insert(new Element('a', {href: e.number + '/', 'class': 'model-link inside'}). + update(e.displayName.escapeHTML()))); + tr.insert(new Element('td', {data: e.duration}). + update(e.durationString.escapeHTML())); + if (isMasterSlaveEnabled) { + tr.insert(new Element('td'). + update(e.builtOn ? new Element('a', {href: rootURL + '/computer/' + e.builtOn, 'class': 'model-link inside'}).update(e.builtOnStr) : e.builtOnStr)); + } + p.insert(tr); + Behaviour.applySubtree(tr); + } + ts_refresh(p); +} diff --git a/core/src/main/resources/hudson/model/Job/configure-entries.jelly b/core/src/main/resources/hudson/model/Job/configure-entries.jelly index 426e0d7040..efc3156081 100644 --- a/core/src/main/resources/hudson/model/Job/configure-entries.jelly +++ b/core/src/main/resources/hudson/model/Job/configure-entries.jelly @@ -26,4 +26,4 @@ THE SOFTWARE. derived class should override this JSP to put additional entries to the config page. --> - \ No newline at end of file + diff --git a/core/src/main/resources/hudson/model/Job/configure.jelly b/core/src/main/resources/hudson/model/Job/configure.jelly index 7a3c91f76c..9536d55a4e 100644 --- a/core/src/main/resources/hudson/model/Job/configure.jelly +++ b/core/src/main/resources/hudson/model/Job/configure.jelly @@ -26,9 +26,18 @@ THE SOFTWARE. Config page. derived class specific entries should go to configure-entries.jsp --> - + - + + + + + + + + + + @@ -56,8 +65,8 @@ THE SOFTWARE. - - + + diff --git a/core/src/main/resources/hudson/model/Job/index.jelly b/core/src/main/resources/hudson/model/Job/index.jelly index 212dc1a443..13be00f033 100644 --- a/core/src/main/resources/hudson/model/Job/index.jelly +++ b/core/src/main/resources/hudson/model/Job/index.jelly @@ -23,7 +23,7 @@ THE SOFTWARE. --> - + diff --git a/core/src/main/resources/hudson/model/Job/newInstanceDetail.jelly b/core/src/main/resources/hudson/model/Job/newInstanceDetail.jelly index d21d52fcd0..5eab02d77c 100644 --- a/core/src/main/resources/hudson/model/Job/newInstanceDetail.jelly +++ b/core/src/main/resources/hudson/model/Job/newInstanceDetail.jelly @@ -23,7 +23,7 @@ THE SOFTWARE. --> - + - +

${%Jenkins is going to shut down. No further builds will be performed.} - + (${%cancel})