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
-[](https://jenkins.io/changelog/)
-[](https://jenkins.io/changelog-stable/)
[](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.antant
- 1.9.14
+ 1.10.7commons-io
@@ -162,7 +162,7 @@ THE SOFTWARE.
commons-codeccommons-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.2592.5.6.SEC032.4.12
+ 2.2
@@ -75,12 +76,12 @@ THE SOFTWARE.
org.jenkins-civersion-number
- 1.6
+ 1.7org.jenkins-cicrypto-util
- 1.1
+ 1.5org.jvnet.hudson
@@ -189,10 +190,16 @@ THE SOFTWARE.
jenkins-stapler-support1.1
+
+ org.hamcrest
+ hamcrest
+ ${hamcrest.version}
+ test
+ org.hamcresthamcrest-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.5jaxen
@@ -600,6 +596,11 @@ THE SOFTWARE.
spotbugs-annotationstrue
+
+ 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 super String> 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 extends PluginManager> klass,
- @Nonnull Jenkins jenkins) throws ReflectiveOperationException {
+ @NonNull
+ PluginManager doCreate(@NonNull Class extends PluginManager> klass,
+ @NonNull Jenkins jenkins) throws ReflectiveOperationException {
return klass.getConstructor(Jenkins.class).newInstance(jenkins);
}
},
SC_FILE {
@Override
- @Nonnull PluginManager doCreate(@Nonnull Class extends PluginManager> klass,
- @Nonnull Jenkins jenkins) throws ReflectiveOperationException {
+ @NonNull PluginManager doCreate(@NonNull Class extends PluginManager> 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 extends PluginManager> klass,
- @Nonnull Jenkins jenkins) throws ReflectiveOperationException {
+ @NonNull PluginManager doCreate(@NonNull Class extends PluginManager> klass,
+ @NonNull Jenkins jenkins) throws ReflectiveOperationException {
return klass.getConstructor(File.class).newInstance(jenkins.getRootDir());
}
};
- final @CheckForNull PluginManager create(@Nonnull Class extends PluginManager> klass,
- @Nonnull Jenkins jenkins) throws ReflectiveOperationException {
+ final @CheckForNull PluginManager create(@NonNull Class extends PluginManager> 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 extends PluginManager> klass,
- @Nonnull Jenkins jenkins) throws ReflectiveOperationException;
+ abstract @NonNull PluginManager doCreate(@NonNull Class extends PluginManager> 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 extends T>... items) {
+ @NonNull
+ public static List join(@NonNull Collection extends T>... items) {
int size = 0;
for (Collection extends T> 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