diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 456f8fca6d..a2ed88450d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -28,16 +28,17 @@ For refactoring and code cleanup changes, exercise the code before and after the ### Proposed changelog entries -- JENKINS-XXXXX, human-readable text +- human-readable text @@ -45,6 +46,11 @@ You may add multiple changelog entries if applicable by adding a new entry to th N/A + + ```[tasklist] ### Submitter checklist - [ ] The Jira issue, if it exists, is well-described. diff --git a/.github/workflows/label-conflicting-pr.yml b/.github/workflows/label-conflicting-pr.yml index ae90f55bc7..8b78edd600 100644 --- a/.github/workflows/label-conflicting-pr.yml +++ b/.github/workflows/label-conflicting-pr.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Label conflicting PRs - uses: eps1lon/actions-label-merge-conflict@v3.0.1 + uses: eps1lon/actions-label-merge-conflict@v3.0.2 with: dirtyLabel: "unresolved-merge-conflict" repoToken: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/publish-release-artifact.yml b/.github/workflows/publish-release-artifact.yml index 31854bf826..ae5e6bf926 100644 --- a/.github/workflows/publish-release-artifact.yml +++ b/.github/workflows/publish-release-artifact.yml @@ -73,7 +73,7 @@ jobs: wget -q https://get.jenkins.io/${REPO}/${PROJECT_VERSION}/${FILE_NAME} - name: Upload Release Asset id: upload-war - uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 + uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -108,7 +108,7 @@ jobs: - name: Upload Release Asset id: upload-deb if: always() - uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 + uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -144,7 +144,7 @@ jobs: - name: Upload Release Asset id: upload-rpm if: always() - uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 + uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -180,7 +180,7 @@ jobs: - name: Upload Release Asset id: upload-msi if: always() - uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 + uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -216,7 +216,7 @@ jobs: - name: Upload Release Asset id: upload-suse-rpm if: always() - uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 + uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.gitpod/Dockerfile b/.gitpod/Dockerfile index f2475612b1..63c4aff7d9 100644 --- a/.gitpod/Dockerfile +++ b/.gitpod/Dockerfile @@ -1,6 +1,6 @@ FROM gitpod/workspace-full -ARG MAVEN_VERSION=3.9.7 +ARG MAVEN_VERSION=3.9.8 RUN brew install gh && \ bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh && sdk install maven ${MAVEN_VERSION} && sdk default maven ${MAVEN_VERSION}" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 77c012b44c..58af2c6585 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ This page provides information about contributing code to the Jenkins core codeb 1. Fork the repository on GitHub 2. Clone the forked repository to your machine 3. Install the necessary development tools. In order to develop Jenkins, you need the following: - - Java Development Kit (JDK) 11, 17 or 21. + - Java Development Kit (JDK) 17 or 21. In the Jenkins project we usually use [Eclipse Temurin](https://adoptium.net/) or [OpenJDK](https://openjdk.java.net/), but you can use other JDKs as well. - Apache Maven 3.8.1 or above. You can [download Maven here](https://maven.apache.org/download.cgi). In the Jenkins project we usually use the most recent Maven release. diff --git a/Jenkinsfile b/Jenkinsfile index 8b87983f43..2380b3b66d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,12 +14,12 @@ properties([ def axes = [ platforms: ['linux', 'windows'], - jdks: [11, 17, 21], + jdks: [17, 21], ] stage('Record build') { retry(conditions: [kubernetesAgent(handleNonKubernetes: true), nonresumable()], count: 2) { - node('maven-11') { + node('maven-17') { infra.checkoutSCM() /* diff --git a/ath.sh b/ath.sh index 3ab8d159b6..3cd7dfd86d 100644 --- a/ath.sh +++ b/ath.sh @@ -6,7 +6,7 @@ set -o xtrace cd "$(dirname "$0")" # https://github.com/jenkinsci/acceptance-test-harness/releases -export ATH_VERSION=5858.v4b_c4e3b_16099 +export ATH_VERSION=5911.v5f88b_6d0c450 if [[ $# -eq 0 ]]; then export JDK=17 diff --git a/bom/pom.xml b/bom/pom.xml index d3aed901a3..8f8e6412f7 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -40,7 +40,7 @@ THE SOFTWARE. 2.0.0-M2 2.0.13 - 1869.v3605a_771fb_e3 + 1892.v73465f3d074d 2.4.21 @@ -56,7 +56,7 @@ THE SOFTWARE. org.springframework spring-framework-bom - 5.3.36 + 5.3.37 pom import @@ -64,7 +64,7 @@ THE SOFTWARE. org.springframework.security spring-security-bom - 5.8.12 + 5.8.13 pom import @@ -82,7 +82,7 @@ THE SOFTWARE. com.google.guava guava - 33.2.0-jre + 33.2.1-jre @@ -113,7 +113,7 @@ THE SOFTWARE. commons-codec commons-codec - 1.17.0 + 1.17.1 commons-collections @@ -376,7 +376,7 @@ THE SOFTWARE. commons-logging commons-logging - 1.3.2 + 1.3.3 provided diff --git a/cli/pom.xml b/cli/pom.xml index 14d6323ca7..c037ed4a0e 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -119,7 +119,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.3 + 3.6.0 diff --git a/core/pom.xml b/core/pom.xml index 61c48e4897..95c2891649 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -507,6 +507,62 @@ THE SOFTWARE. + + org.apache.maven.plugins + maven-enforcer-plugin + + + + enforce-banned-dependencies + + enforce + + + + + + + com.fasterxml.jackson.* + com.github.ben-manes.caffeine:caffeine + com.github.jnr:jnr-posix + com.github.mwiede:jsch + com.google.code.gson:gson + com.jayway.jsonpath:json-path + commons-httpclient:commons-httpclient + com.sun.activation:javax.activation + com.sun.mail:javax.mail + com.sun.xml.bind:jaxb-impl + io.jsonwebtoken + + jakarta.activation:jakarta.activation-api:*:jar:compile + jakarta.activation:jakarta.activation-api:*:jar:runtime + jakarta.mail:jakarta.mail-api + javax.activation:javax.activation-api + javax.mail:javax.mail-api + javax.xml.bind:jaxb-api + joda-time:joda-time + + net.bytebuddy:byte-buddy:*:jar:compile + net.bytebuddy:byte-buddy:*:jar:runtime + net.i2p.crypto:eddsa + net.minidev + org.apache.commons:commons-lang3 + org.apache.commons:commons-text + org.apache.httpcomponents + org.bouncycastle + org.eclipse.angus:angus-activation + org.eclipse.angus:angus-mail + org.glassfish.jersey.* + org.json:json + org.ow2.asm + org.yaml:snakeyaml + + + + + + + org.codehaus.mojo build-helper-maven-plugin diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index d9b37f0fac..3d6edf832f 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -252,7 +252,7 @@ public class ClassicPluginStrategy implements PluginStrategy { for (Dependency d : DetachedPluginsUtil.getImpliedDependencies(pluginName, jenkinsVersion)) { LOGGER.fine(() -> "implied dep " + pluginName + " → " + d.shortName); - pluginManager.considerDetachedPlugin(d.shortName); + pluginManager.considerDetachedPlugin(d.shortName, pluginName); optionalDependencies.add(d); } } @@ -269,17 +269,17 @@ public class ClassicPluginStrategy implements PluginStrategy { } /** - * @deprecated since TODO use {@link #createClassLoader(String, List, ClassLoader, Attributes)} + * @deprecated since 2.459 use {@link #createClassLoader(String, List, ClassLoader, Attributes)} */ - @Deprecated(since = "TODO") + @Deprecated(since = "2.459") protected ClassLoader createClassLoader(List paths, ClassLoader parent) throws IOException { return createClassLoader(paths, parent, null); } /** - * @deprecated since TODO use {@link #createClassLoader(String, List, ClassLoader, Attributes)} + * @deprecated since 2.459 use {@link #createClassLoader(String, List, ClassLoader, Attributes)} */ - @Deprecated(since="TODO") + @Deprecated(since="2.459") protected ClassLoader createClassLoader(List paths, ClassLoader parent, Attributes atts) throws IOException { // generate a legacy id so at least we can track to something return createClassLoader("unidentified-" + UUID.randomUUID(), paths, parent, atts); @@ -287,7 +287,7 @@ public class ClassicPluginStrategy implements PluginStrategy { /** * Creates a classloader that can load all the specified jar files and delegate to the given parent. - * @since TODO + * @since 2.459 */ protected ClassLoader createClassLoader(String name, List paths, ClassLoader parent, Attributes atts) throws IOException { boolean usePluginFirstClassLoader = diff --git a/core/src/main/java/hudson/DependencyRunner.java b/core/src/main/java/hudson/DependencyRunner.java index b7db91fb94..f577440659 100644 --- a/core/src/main/java/hudson/DependencyRunner.java +++ b/core/src/main/java/hudson/DependencyRunner.java @@ -58,7 +58,7 @@ public class DependencyRunner implements Runnable { // Get all top-level projects LOGGER.fine("assembling top level projects"); for (AbstractProject p : Jenkins.get().allItems(AbstractProject.class)) - if (p.getUpstreamProjects().size() == 0) { + if (p.getUpstreamProjects().isEmpty()) { LOGGER.fine("adding top level project " + p.getName()); topLevelProjects.add(p); } else { diff --git a/core/src/main/java/hudson/EnvVars.java b/core/src/main/java/hudson/EnvVars.java index 286151e46b..97def5f11a 100644 --- a/core/src/main/java/hudson/EnvVars.java +++ b/core/src/main/java/hudson/EnvVars.java @@ -114,8 +114,7 @@ public class EnvVars extends TreeMap { // because of the backward compatibility, some parts of Jenkins passes // EnvVars as Map so downcasting is safer. - if (m instanceof EnvVars) { - EnvVars lhs = (EnvVars) m; + if (m instanceof EnvVars lhs) { this.platform = lhs.platform; } } diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index b0cf56bf90..06773dd9a9 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -31,8 +31,6 @@ import static hudson.Util.fixEmpty; import static hudson.Util.fixEmptyAndTrim; import com.google.common.annotations.VisibleForTesting; -import com.jcraft.jzlib.GZIPInputStream; -import com.jcraft.jzlib.GZIPOutputStream; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -61,7 +59,6 @@ import hudson.util.DirScanner; import hudson.util.ExceptionCatchingThreadFactory; import hudson.util.FileVisitor; import hudson.util.FormValidation; -import hudson.util.HeadBufferingStream; import hudson.util.IOUtils; import hudson.util.NamingThreadFactory; import hudson.util.io.Archiver; @@ -124,6 +121,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import jenkins.MasterToSlaveFileCallable; import jenkins.SlaveToMasterFileCallable; import jenkins.model.Jenkins; @@ -131,14 +130,14 @@ import jenkins.security.MasterToSlaveCallable; import jenkins.util.ContextResettingExecutorService; import jenkins.util.SystemProperties; import jenkins.util.VirtualFile; -import org.apache.commons.compress.archivers.tar.TarArchiveEntry; -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.fileupload2.core.FileItem; import org.apache.commons.io.input.CountingInputStream; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; import org.apache.tools.ant.types.FileSet; +import org.apache.tools.tar.TarEntry; +import org.apache.tools.tar.TarInputStream; import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipFile; import org.jenkinsci.remoting.RoleChecker; @@ -322,7 +321,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { buf.append(m.group(1)); path = path.substring(m.end()); } - boolean isAbsolute = buf.length() > 0; + boolean isAbsolute = !buf.isEmpty(); // Split remaining path into tokens, trimming any duplicate or trailing separators List tokens = new ArrayList<>(); int s = 0, end = path.length(); @@ -367,7 +366,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { } // Recombine tokens for (String token : tokens) buf.append(token); - if (buf.length() == 0) buf.append('.'); + if (buf.isEmpty()) buf.append('.'); return buf.toString(); } @@ -888,15 +887,8 @@ public final class FilePath implements SerializableOnlyOverRemoting { }, GZIP { @Override - public InputStream extract(InputStream _in) throws IOException { - HeadBufferingStream in = new HeadBufferingStream(_in, SIDE_BUFFER_SIZE); - try { - return new GZIPInputStream(in, 8192, true); - } catch (IOException e) { - // various people reported "java.io.IOException: Not in GZIP format" here, so diagnose this problem better - in.fillSide(); - throw new IOException(e.getMessage() + "\nstream=" + Util.toHexString(in.getSideBuffer()), e); - } + public InputStream extract(InputStream in) throws IOException { + return new GZIPInputStream(new BufferedInputStream(in)); } @Override @@ -1005,8 +997,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { } } - if (con instanceof HttpURLConnection) { - HttpURLConnection httpCon = (HttpURLConnection) con; + if (con instanceof HttpURLConnection httpCon) { int responseCode = httpCon.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP) { @@ -1453,7 +1444,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { public Void invoke(File f, VirtualChannel channel) throws IOException { for (File file : listParentFiles(f)) { if (file.getName().startsWith(f.getName() + WorkspaceList.COMBINATOR)) { - Util.deleteRecursive(file.toPath(), path -> path.toFile()); + Util.deleteRecursive(file.toPath(), Path::toFile); } } @@ -1484,7 +1475,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { @Override public Void invoke(File f, VirtualChannel channel) throws IOException { - Util.deleteRecursive(fileToPath(f), path -> path.toFile()); + Util.deleteRecursive(fileToPath(f), Path::toFile); return null; } } @@ -1501,7 +1492,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { @Override public Void invoke(File f, VirtualChannel channel) throws IOException { - Util.deleteContentsRecursive(fileToPath(f), path -> path.toFile()); + Util.deleteContentsRecursive(fileToPath(f), Path::toFile); return null; } } @@ -2471,7 +2462,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { @Override public Void invoke(File f, VirtualChannel channel) throws IOException { try (OutputStream os = p.getOut(); - OutputStream out = new java.util.zip.GZIPOutputStream(os, 8192); + OutputStream out = new GZIPOutputStream(os, 8192); RandomAccessFile raf = new RandomAccessFile(f, "r")) { raf.seek(offset); byte[] buf = new byte[8192]; @@ -3079,14 +3070,13 @@ public final class FilePath implements SerializableOnlyOverRemoting { /** * Reads from a tar stream and stores obtained files to the base dir. - * Supports large files > 10 GB since 1.627 when this was migrated to use commons-compress. + * Supports large files > 10 GB since 1.627. */ private static void readFromTar(String name, File baseDir, InputStream in, Charset filenamesEncoding) throws IOException { - // TarInputStream t = new TarInputStream(in); - try (TarArchiveInputStream t = new TarArchiveInputStream(in, filenamesEncoding.name())) { - TarArchiveEntry te; - while ((te = t.getNextTarEntry()) != null) { + try (TarInputStream t = new TarInputStream(in, filenamesEncoding.name())) { + TarEntry te; + while ((te = t.getNextEntry()) != null) { File f = new File(baseDir, te.getName()); if (!f.toPath().normalize().startsWith(baseDir.toPath())) { throw new IOException( diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index b02bd2b546..fcc0b3a8ec 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -317,8 +317,7 @@ public class Functions { */ public static Class getTypeParameter(Class c, Class base, int n) { Type parameterization = Types.getBaseClass(c, base); - if (parameterization instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType) parameterization; + if (parameterization instanceof ParameterizedType pt) { return Types.erasure(Types.getTypeArgument(pt, n)); } else { throw new AssertionError(c + " doesn't properly parameterize " + base); @@ -1423,7 +1422,7 @@ public class Functions { StringBuilder buf = new StringBuilder(); Item i = p; while (true) { - if (buf.length() > 0) buf.insert(0, separationString); + if (!buf.isEmpty()) buf.insert(0, separationString); buf.insert(0, useDisplayName ? i.getDisplayName() : i.getName()); ItemGroup gr = i.getParent(); @@ -1874,7 +1873,7 @@ public class Functions { for (String s : components) { if (s.isEmpty()) continue; - if (buf.length() > 0) { + if (!buf.isEmpty()) { if (buf.charAt(buf.length() - 1) != '/') buf.append('/'); if (s.charAt(0) == '/') s = s.substring(1); @@ -1980,8 +1979,7 @@ public class Functions { @Deprecated public String getCheckUrl(String userDefined, Object descriptor, String field) { if (userDefined != null || field == null) return userDefined; - if (descriptor instanceof Descriptor) { - Descriptor d = (Descriptor) descriptor; + if (descriptor instanceof Descriptor d) { return d.getCheckUrl(field); } return null; @@ -1994,8 +1992,7 @@ public class Functions { public void calcCheckUrl(Map attributes, String userDefined, Object descriptor, String field) { if (userDefined != null || field == null) return; - if (descriptor instanceof Descriptor) { - Descriptor d = (Descriptor) descriptor; + if (descriptor instanceof Descriptor d) { CheckMethod m = d.getCheckMethod(field); attributes.put("checkUrl", m.toStemUrl()); attributes.put("checkDependsOn", m.getDependsOn()); @@ -2058,7 +2055,7 @@ public class Functions { * Prepend a prefix only when there's the specified body. */ public String prepend(String prefix, String body) { - if (body != null && body.length() > 0) + if (body != null && !body.isEmpty()) return prefix + body; return body; } diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 5d80c6e623..75dd52c4d7 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -615,7 +615,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas }}); } - void considerDetachedPlugin(String shortName) { + void considerDetachedPlugin(String shortName, String source) { if (new File(rootDir, shortName + ".jpi").isFile() || new File(rootDir, shortName + ".hpi").isFile() || new File(rootDir, shortName + ".jpl").isFile() || @@ -627,7 +627,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas for (String loadedFile : loadPluginsFromWar(getDetachedLocation(), (dir, name) -> normalisePluginName(name).equals(shortName))) { String loaded = normalisePluginName(loadedFile); File arc = new File(rootDir, loaded + ".jpi"); - LOGGER.info(() -> "Loading a detached plugin as a dependency: " + arc); + LOGGER.info(() -> "Loading a detached plugin " + arc + " as a dependency of " + source); try { plugins.add(strategy.createPluginWrapper(arc)); } catch (IOException e) { @@ -716,6 +716,10 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } Manifest manifest = parsePluginManifest(hpiResUrl); + if (manifest == null) { + return; + } + String dependencySpec = manifest.getMainAttributes().getValue("Plugin-Dependencies"); if (dependencySpec != null) { String[] dependencyTokens = dependencySpec.split(","); diff --git a/core/src/main/java/hudson/cli/CLIAction.java b/core/src/main/java/hudson/cli/CLIAction.java index 4265a6283a..9e29b14156 100644 --- a/core/src/main/java/hudson/cli/CLIAction.java +++ b/core/src/main/java/hudson/cli/CLIAction.java @@ -217,7 +217,7 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy { @Override public Object getTarget() { StaplerRequest req = Stapler.getCurrentRequest(); - if (req.getRestOfPath().length() == 0 && "POST".equals(req.getMethod())) { + if (req.getRestOfPath().isEmpty() && "POST".equals(req.getMethod())) { // CLI connection request if ("false".equals(req.getParameter("remoting"))) { throw new PlainCliEndpointResponse(); diff --git a/core/src/main/java/hudson/cli/GroovyshCommand.java b/core/src/main/java/hudson/cli/GroovyshCommand.java index 8bc4d522e2..18cbc6da75 100644 --- a/core/src/main/java/hudson/cli/GroovyshCommand.java +++ b/core/src/main/java/hudson/cli/GroovyshCommand.java @@ -77,7 +77,7 @@ public class GroovyshCommand extends CLICommand { StringBuilder commandLine = new StringBuilder(); for (String arg : args) { - if (commandLine.length() > 0) { + if (!commandLine.isEmpty()) { commandLine.append(" "); } commandLine.append(arg); diff --git a/core/src/main/java/hudson/console/AnnotatedLargeText.java b/core/src/main/java/hudson/console/AnnotatedLargeText.java index 8e0a59bee2..4e0d3b9908 100644 --- a/core/src/main/java/hudson/console/AnnotatedLargeText.java +++ b/core/src/main/java/hudson/console/AnnotatedLargeText.java @@ -28,8 +28,6 @@ package hudson.console; import static java.lang.Math.abs; -import com.jcraft.jzlib.GZIPInputStream; -import com.jcraft.jzlib.GZIPOutputStream; import edu.umd.cs.findbugs.annotations.CheckReturnValue; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.remoting.ObjectInputStreamEx; @@ -45,6 +43,8 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.concurrent.TimeUnit; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; diff --git a/core/src/main/java/hudson/console/ConsoleNote.java b/core/src/main/java/hudson/console/ConsoleNote.java index 20ded06e98..633a5ed547 100644 --- a/core/src/main/java/hudson/console/ConsoleNote.java +++ b/core/src/main/java/hudson/console/ConsoleNote.java @@ -24,8 +24,6 @@ package hudson.console; -import com.jcraft.jzlib.GZIPInputStream; -import com.jcraft.jzlib.GZIPOutputStream; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.ExtensionPoint; import hudson.Functions; @@ -50,6 +48,8 @@ import java.util.Arrays; import java.util.Base64; import java.util.Collection; import java.util.List; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import jenkins.model.Jenkins; import jenkins.security.HMACConfidentialKey; import jenkins.util.JenkinsJVM; diff --git a/core/src/main/java/hudson/console/ExpandableDetailsNote.java b/core/src/main/java/hudson/console/ExpandableDetailsNote.java index 1f558e6a26..beb623b503 100644 --- a/core/src/main/java/hudson/console/ExpandableDetailsNote.java +++ b/core/src/main/java/hudson/console/ExpandableDetailsNote.java @@ -26,8 +26,8 @@ package hudson.console; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; -import hudson.Functions; import hudson.MarkupText; +import hudson.Util; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -53,7 +53,8 @@ public class ExpandableDetailsNote extends ConsoleNote { @Override public ConsoleAnnotator annotate(Object context, MarkupText text, int charPos) { text.addMarkup(charPos, - "
" + html + "
"); + "
" + html + "
"); return null; } diff --git a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java index b389221f76..e2bfa4678f 100644 --- a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java +++ b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java @@ -218,7 +218,7 @@ public class OldDataMonitor extends AdministrativeMonitor { buf.append(e.getClass().getSimpleName()).append(": ").append(e.getMessage()); } } - if (buf.length() == 0) return; + if (buf.isEmpty()) return; Jenkins j = Jenkins.getInstanceOrNull(); if (j == null) { // Need this path, at least for unit tests, but also in case of very broken startup // Startup failed, something is very broken, so report what we can. diff --git a/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java b/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java index 8873925847..c305e9a6fe 100644 --- a/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java +++ b/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java @@ -18,8 +18,8 @@ import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.UncaughtExceptionFilter; import org.kohsuke.stapler.WebApp; -import org.kohsuke.stapler.compression.CompressionFilter; /** * Deals with exceptions that get thrown all the way up to the Stapler rendering layer. @@ -30,7 +30,7 @@ public class InstallUncaughtExceptionHandler { @Initializer public static void init(final Jenkins j) throws IOException { - CompressionFilter.setUncaughtExceptionHandler(j.servletContext, (e, context, req, rsp) -> handleException(j, e, req, rsp, 500)); + UncaughtExceptionFilter.setUncaughtExceptionHandler(j.servletContext, (e, context, req, rsp) -> handleException(j, e, req, rsp, 500)); try { Thread.setDefaultUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler()); LOGGER.log(Level.FINE, "Successfully installed a global UncaughtExceptionHandler."); diff --git a/core/src/main/java/hudson/lifecycle/ExitLifecycle.java b/core/src/main/java/hudson/lifecycle/ExitLifecycle.java index f8fcc3abef..038dafc442 100644 --- a/core/src/main/java/hudson/lifecycle/ExitLifecycle.java +++ b/core/src/main/java/hudson/lifecycle/ExitLifecycle.java @@ -26,6 +26,7 @@ package hudson.lifecycle; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; +import hudson.util.BootFailure; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.Jenkins; @@ -72,4 +73,9 @@ public class ExitLifecycle extends Lifecycle { System.exit(exitOnRestart); } + + @Override + public void onBootFailure(BootFailure problem) { + restart(); + } } diff --git a/core/src/main/java/hudson/lifecycle/Lifecycle.java b/core/src/main/java/hudson/lifecycle/Lifecycle.java index dbc53d2b50..fcd7769aef 100644 --- a/core/src/main/java/hudson/lifecycle/Lifecycle.java +++ b/core/src/main/java/hudson/lifecycle/Lifecycle.java @@ -32,6 +32,8 @@ import hudson.PluginManager; import hudson.Util; import hudson.init.InitMilestone; import hudson.init.Initializer; +import hudson.util.BootFailure; +import hudson.util.JenkinsReloadFailed; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; @@ -324,6 +326,14 @@ public abstract class Lifecycle implements ExtensionPoint { return true; } + /** + * Called when Jenkins has failed to boot. + * @param problem a boot failure (could be {@link JenkinsReloadFailed}) + * @since TODO + */ + public void onBootFailure(BootFailure problem) { + } + @Restricted(NoExternalUse.class) public static final class PlaceholderLifecycle extends ExitLifecycle { diff --git a/core/src/main/java/hudson/logging/LogRecorder.java b/core/src/main/java/hudson/logging/LogRecorder.java index 23ebc72cde..86aea65750 100644 --- a/core/src/main/java/hudson/logging/LogRecorder.java +++ b/core/src/main/java/hudson/logging/LogRecorder.java @@ -393,7 +393,7 @@ public class LogRecorder extends AbstractModelObject implements Loadable, Saveab void broadcast() { for (Computer c : Jenkins.get().getComputers()) { - if (c.getName().length() > 0) { // i.e. not master + if (!c.getName().isEmpty()) { // i.e. not master VirtualChannel ch = c.getChannel(); if (ch != null) { try { @@ -595,7 +595,7 @@ public class LogRecorder extends AbstractModelObject implements Loadable, Saveab } }); for (Computer c : Jenkins.get().getComputers()) { - if (c.getName().length() == 0) { + if (c.getName().isEmpty()) { continue; // master } List recs = new ArrayList<>(); diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java index bcd29082eb..85d62df788 100644 --- a/core/src/main/java/hudson/model/AbstractBuild.java +++ b/core/src/main/java/hudson/model/AbstractBuild.java @@ -607,8 +607,7 @@ public abstract class AbstractBuild

, R extends A final Node currentNode = getCurrentNode(); Launcher l = currentNode.createLauncher(listener); - if (project instanceof BuildableItemWithBuildWrappers) { - BuildableItemWithBuildWrappers biwbw = (BuildableItemWithBuildWrappers) project; + if (project instanceof BuildableItemWithBuildWrappers biwbw) { for (BuildWrapper bw : biwbw.getBuildWrappersList()) l = bw.decorateLauncher(AbstractBuild.this, l, listener); } diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java index 0649bdb33b..adebec8f28 100644 --- a/core/src/main/java/hudson/model/AbstractItem.java +++ b/core/src/main/java/hudson/model/AbstractItem.java @@ -25,7 +25,6 @@ package hudson.model; -import static hudson.model.queue.Executables.getParentOf; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import com.infradna.tool.bridge_method_injector.WithBridgeMethods; @@ -39,9 +38,6 @@ import hudson.XmlFile; import hudson.cli.declarative.CLIResolver; import hudson.model.listeners.ItemListener; import hudson.model.listeners.SaveableListener; -import hudson.model.queue.SubTask; -import hudson.model.queue.Tasks; -import hudson.model.queue.WorkUnit; import hudson.security.ACL; import hudson.security.ACLContext; import hudson.security.AccessControlled; @@ -57,12 +53,8 @@ import java.io.OutputStream; import java.nio.charset.Charset; import java.nio.file.Files; import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; import java.util.ListIterator; -import java.util.Map; -import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -551,8 +543,7 @@ public abstract class AbstractItem extends Actionable implements Loadable, Item, List ancestors = req.getAncestors(); if (!ancestors.isEmpty()) { Ancestor last = ancestors.get(ancestors.size() - 1); - if (last.getObject() instanceof View) { - View view = (View) last.getObject(); + if (last.getObject() instanceof View view) { if (view.getOwner().getItemGroup() == getParent() && !view.isDefault()) { // Showing something inside a view, so should use that as the base URL. String prefix = req.getContextPath() + "/"; @@ -706,11 +697,13 @@ public abstract class AbstractItem extends Actionable implements Loadable, Item, * *

* Any exception indicates the deletion has failed, but {@link AbortException} would prevent the caller - * from showing the stack trace. This + * from showing the stack trace. + * @see ItemDeletion */ @Override public void delete() throws IOException, InterruptedException { checkPermission(DELETE); + ItemListener.checkBeforeDelete(this); boolean responsibleForAbortingBuilds = !ItemDeletion.contains(this); boolean ownsRegistration = ItemDeletion.register(this); if (!ownsRegistration && ItemDeletion.isRegistered(this)) { @@ -720,87 +713,7 @@ public abstract class AbstractItem extends Actionable implements Loadable, Item, try { // if a build is in progress. Cancel it. if (responsibleForAbortingBuilds || ownsRegistration) { - Queue queue = Queue.getInstance(); - if (this instanceof Queue.Task) { - // clear any items in the queue so they do not get picked up - queue.cancel((Queue.Task) this); - } - // now cancel any child items - this happens after ItemDeletion registration, so we can use a snapshot - for (Queue.Item i : queue.getItems()) { - Item item = Tasks.getItemOf(i.task); - while (item != null) { - if (item == this) { - if (!queue.cancel(i)) { - LOGGER.warning(() -> "failed to cancel " + i); - } - break; - } - if (item.getParent() instanceof Item) { - item = (Item) item.getParent(); - } else { - break; - } - } - } - // interrupt any builds in progress (and this should be a recursive test so that folders do not pay - // the 15 second delay for every child item). This happens after queue cancellation, so will be - // a complete set of builds in flight - Map buildsInProgress = new LinkedHashMap<>(); - for (Computer c : Jenkins.get().getComputers()) { - for (Executor e : c.getAllExecutors()) { - final WorkUnit workUnit = e.getCurrentWorkUnit(); - final Queue.Executable executable = workUnit != null ? workUnit.getExecutable() : null; - final SubTask subtask = executable != null ? getParentOf(executable) : null; - - if (subtask != null) { - Item item = Tasks.getItemOf(subtask); - while (item != null) { - if (item == this) { - buildsInProgress.put(e, e.getCurrentExecutable()); - e.interrupt(Result.ABORTED); - break; - } - if (item.getParent() instanceof Item) { - item = (Item) item.getParent(); - } else { - break; - } - } - } - } - } - if (!buildsInProgress.isEmpty()) { - // give them 15 seconds or so to respond to the interrupt - long expiration = System.nanoTime() + TimeUnit.SECONDS.toNanos(15); - // comparison with executor.getCurrentExecutable() == computation currently should always be true - // as we no longer recycle Executors, but safer to future-proof in case we ever revisit recycling - while (!buildsInProgress.isEmpty() && expiration - System.nanoTime() > 0L) { - // we know that ItemDeletion will prevent any new builds in the queue - // ItemDeletion happens-before Queue.cancel so we know that the Queue will stay clear - // Queue.cancel happens-before collecting the buildsInProgress list - // thus buildsInProgress contains the complete set we need to interrupt and wait for - for (Iterator> iterator = - buildsInProgress.entrySet().iterator(); - iterator.hasNext(); ) { - Map.Entry entry = iterator.next(); - // comparison with executor.getCurrentExecutable() == executable currently should always be - // true as we no longer recycle Executors, but safer to future-proof in case we ever - // revisit recycling. - if (!entry.getKey().isAlive() - || entry.getValue() != entry.getKey().getCurrentExecutable()) { - iterator.remove(); - } - // I don't know why, but we have to keep interrupting - entry.getKey().interrupt(Result.ABORTED); - } - Thread.sleep(50L); - } - if (!buildsInProgress.isEmpty()) { - throw new Failure(Messages.AbstractItem_FailureToStopBuilds( - buildsInProgress.size(), getFullDisplayName() - )); - } - } + ItemDeletion.cancelBuildsInProgress(this); } if (this instanceof ItemGroup) { // delete individual items first @@ -937,6 +850,7 @@ public abstract class AbstractItem extends Actionable implements Loadable, Item, // if everything went well, commit this new version out.commit(); SaveableListener.fireOnChange(this, getConfigFile()); + ItemListener.fireOnUpdated(this); } finally { out.abort(); // don't leave anything behind diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index 5de1775b11..c568f8df8b 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -518,8 +518,7 @@ public abstract class AbstractProject

, R extends Executor e = Executor.currentExecutor(); if (e != null) { Executable exe = e.getCurrentExecutable(); - if (exe instanceof AbstractBuild) { - AbstractBuild b = (AbstractBuild) exe; + if (exe instanceof AbstractBuild b) { if (b.getProject() == this) return b; } @@ -1012,6 +1011,7 @@ public abstract class AbstractProject

, R extends * null if no information is available (for example, * if no build was done yet.) */ + @SuppressWarnings("deprecation") @Override public Node getLastBuiltOn() { // where was it built on? diff --git a/core/src/main/java/hudson/model/AdministrativeMonitor.java b/core/src/main/java/hudson/model/AdministrativeMonitor.java index 7a9a0dde97..bdbfb48027 100644 --- a/core/src/main/java/hudson/model/AdministrativeMonitor.java +++ b/core/src/main/java/hudson/model/AdministrativeMonitor.java @@ -183,7 +183,7 @@ public abstract class AdministrativeMonitor extends AbstractModelObject implemen /** * Required permission to view this admin monitor. - * By default {@link Jenkins#ADMINISTER}, but {@link Jenkins#SYSTEM_READ} is also supported. + * By default {@link Jenkins#ADMINISTER}, but {@link Jenkins#SYSTEM_READ} or {@link Jenkins#MANAGE} are also supported. *

* Changing this permission check to return {@link Jenkins#SYSTEM_READ} will make the active * administrative monitor appear on {@code manage.jelly} and on the globally visible @@ -191,23 +191,69 @@ public abstract class AdministrativeMonitor extends AbstractModelObject implemen * {@link #doDisable(StaplerRequest, StaplerResponse)} will still always require Administer permission. *

*

+ * This method only allows for a single permission to be returned. If more complex permission checks are required, + * override {@link #checkRequiredPermission()} and {@link #hasRequiredPermission()} instead. + *

+ *

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

+ * @since 2.233 + * @deprecated Callers should use {@link #checkRequiredPermission()} or {@link #hasRequiredPermission()}. */ + @Deprecated public Permission getRequiredPermission() { return Jenkins.ADMINISTER; } + /** + * Checks if the current user has the minimum required permission to view this administrative monitor. + *

+ * Subclasses may override this method and {@link #hasRequiredPermission()} instead of {@link #getRequiredPermission()} to perform more complex permission checks, + * for example, checking either {@link Jenkins#MANAGE} or {@link Jenkins#SYSTEM_READ}. + *

+ * @see #getRequiredPermission() + * @see #hasRequiredPermission() + * @since 2.468 + */ + public void checkRequiredPermission() { + Jenkins.get().checkPermission(getRequiredPermission()); + } + + /** + * Checks if the current user has the minimum required permission to view this administrative monitor. + *

+ * Subclasses may override this method and {@link #checkRequiredPermission} instead of {@link #getRequiredPermission()} to perform more complex permission checks, + * for example, checking either {@link Jenkins#MANAGE} or {@link Jenkins#SYSTEM_READ}. + *

+ * @see #getRequiredPermission() + * @see #checkRequiredPermission() + * @since 2.468 + */ + public boolean hasRequiredPermission() { + return Jenkins.get().hasPermission(getRequiredPermission()); + } + + /** + * Checks if the current user has the minimum required permission to view any administrative monitor. + * + * @return true if the current user has the minimum required permission to view any administrative monitor. + * + * @since 2.468 + */ + public static boolean hasPermissionToDisplay() { + return Jenkins.get().hasAnyPermission(Jenkins.SYSTEM_READ, Jenkins.MANAGE); + } + /** * Ensure that URLs in this administrative monitor are only accessible to users with {@link #getRequiredPermission()}. */ @Override @Restricted(NoExternalUse.class) public Object getTarget() { - Jenkins.get().checkPermission(getRequiredPermission()); + checkRequiredPermission(); return this; } diff --git a/core/src/main/java/hudson/model/Api.java b/core/src/main/java/hudson/model/Api.java index 11db76c358..23e7207211 100644 --- a/core/src/main/java/hudson/model/Api.java +++ b/core/src/main/java/hudson/model/Api.java @@ -190,8 +190,7 @@ public class Api extends AbstractModelObject { return; } - // switch to gzipped output - try (OutputStream o = rsp.getCompressedOutputStream(req)) { + try (OutputStream o = rsp.getOutputStream()) { if (isSimpleOutput(result)) { // simple output allowed rsp.setContentType("text/plain;charset=UTF-8"); diff --git a/core/src/main/java/hudson/model/BooleanParameterDefinition.java b/core/src/main/java/hudson/model/BooleanParameterDefinition.java index 5e270ef46d..6e9db32216 100644 --- a/core/src/main/java/hudson/model/BooleanParameterDefinition.java +++ b/core/src/main/java/hudson/model/BooleanParameterDefinition.java @@ -59,8 +59,7 @@ public class BooleanParameterDefinition extends SimpleParameterDefinition { @Override public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) { - if (defaultValue instanceof BooleanParameterValue) { - BooleanParameterValue value = (BooleanParameterValue) defaultValue; + if (defaultValue instanceof BooleanParameterValue value) { return new BooleanParameterDefinition(getName(), value.value, getDescription()); } else { return this; diff --git a/core/src/main/java/hudson/model/BuildAuthorizationToken.java b/core/src/main/java/hudson/model/BuildAuthorizationToken.java index f101eb3d6e..a09ed113e1 100644 --- a/core/src/main/java/hudson/model/BuildAuthorizationToken.java +++ b/core/src/main/java/hudson/model/BuildAuthorizationToken.java @@ -29,7 +29,6 @@ import hudson.Util; import hudson.security.ACL; import java.io.IOException; import javax.servlet.http.HttpServletResponse; -import jenkins.security.ApiTokenProperty; import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -82,10 +81,6 @@ public final class BuildAuthorizationToken { return; } - if (req.getAttribute(ApiTokenProperty.class.getName()) instanceof User) { - return; - } - rsp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); rsp.addHeader("Allow", "POST"); throw HttpResponses.forwardToView(project, "requirePOST.jelly"); diff --git a/core/src/main/java/hudson/model/CauseAction.java b/core/src/main/java/hudson/model/CauseAction.java index eca42ffb69..316cf3a182 100644 --- a/core/src/main/java/hudson/model/CauseAction.java +++ b/core/src/main/java/hudson/model/CauseAction.java @@ -61,8 +61,7 @@ public class CauseAction implements FoldableAction, RunAction2 { private void addCause(Cause c) { synchronized (causeBag) { - Integer cnt = causeBag.get(c); - causeBag.put(c, cnt == null ? 1 : cnt + 1); + causeBag.compute(c, (unused, cnt) -> cnt == null ? 1 : cnt + 1); } } diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 3470ad4d15..14a311381a 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -1457,7 +1457,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces checkPermission(Jenkins.ADMINISTER); rsp.setContentType("text/plain"); - try (PrintWriter w = new PrintWriter(rsp.getCompressedWriter(req))) { + try (PrintWriter w = new PrintWriter(rsp.getWriter())) { VirtualChannel vc = getChannel(); if (vc instanceof Channel) { w.println("Controller to agent"); diff --git a/core/src/main/java/hudson/model/Descriptor.java b/core/src/main/java/hudson/model/Descriptor.java index 817115c3ad..b5eb07784d 100644 --- a/core/src/main/java/hudson/model/Descriptor.java +++ b/core/src/main/java/hudson/model/Descriptor.java @@ -294,8 +294,7 @@ public abstract class Descriptor> implements Loadable, // detect an type error Type bt = Types.getBaseClass(getClass(), Descriptor.class); - if (bt instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType) bt; + if (bt instanceof ParameterizedType pt) { // this 't' is the closest approximation of T of Descriptor. Class t = Types.erasure(pt.getActualTypeArguments()[0]); if (!t.isAssignableFrom(clazz)) @@ -595,6 +594,9 @@ public abstract class Descriptor> implements Loadable, return verifyNewInstance(bindJSON(req, clazz, formData, true)); } } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | RuntimeException e) { + if (e instanceof RuntimeException && e instanceof HttpResponse) { + throw (RuntimeException) e; + } throw new LinkageError("Failed to instantiate " + clazz + " from " + RedactSecretJsonInErrorMessageSanitizer.INSTANCE.sanitize(formData), e); } } @@ -675,7 +677,7 @@ public abstract class Descriptor> implements Loadable, + actualType.getName() + " " + json); } } catch (Exception x) { - LOGGER.log(Level.WARNING, "falling back to default instantiation " + actualType.getName() + " " + json, x); + LOGGER.log(x instanceof HttpResponse ? Level.FINE : Level.WARNING, "falling back to default instantiation " + actualType.getName() + " " + json, x); // If nested objects are not using newInstance, bindJSON will wind up throwing the same exception anyway, // so logging above will result in a duplicated stack trace. // However if they *are* then this is the only way to find errors in that newInstance. @@ -688,8 +690,7 @@ public abstract class Descriptor> implements Loadable, @Override public Object onConvert(Type targetType, Class targetTypeErasure, Object jsonSource) { - if (jsonSource instanceof JSONObject) { - JSONObject json = (JSONObject) jsonSource; + if (jsonSource instanceof JSONObject json) { if (isApplicable(targetTypeErasure, json)) { LOGGER.log(Level.FINE, "switching to newInstance {0} {1}", new Object[] {targetTypeErasure.getName(), json}); try { @@ -895,8 +896,7 @@ public abstract class Descriptor> implements Loadable, protected List getPossibleViewNames(String baseName) { List names = new ArrayList<>(); for (Facet f : WebApp.get(Jenkins.get().servletContext).facets) { - if (f instanceof JellyCompatibleFacet) { - JellyCompatibleFacet jcf = (JellyCompatibleFacet) f; + if (f instanceof JellyCompatibleFacet jcf) { for (String ext : jcf.getScriptExtensions()) names.add(baseName + ext); } @@ -1007,6 +1007,10 @@ public abstract class Descriptor> implements Loadable, if (url != null) return url; url = c.getResource(base + '_' + locale.getLanguage() + ".html"); if (url != null) return url; + if (locale.getLanguage().equals("en")) { + url = c.getResource(base + ".html"); + if (url != null) return url; + } } // default diff --git a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java index 8083efa72d..9d455f4c1b 100644 --- a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java +++ b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java @@ -33,6 +33,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.net.URL; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.LinkOption; import java.nio.file.OpenOption; @@ -90,11 +91,6 @@ public final class DirectoryBrowserSupport implements HttpResponse { private static final Pattern TMPDIR_PATTERN = Pattern.compile(".+@tmp/.*"); - /** - * Escape hatch for the protection against SECURITY-2481. If enabled, the absolute paths on Windows will be allowed. - */ - static final String ALLOW_ABSOLUTE_PATH_PROPERTY_NAME = DirectoryBrowserSupport.class.getName() + ".allowAbsolutePath"; - public final ModelObject owner; public final String title; @@ -230,7 +226,7 @@ public final class DirectoryBrowserSupport implements HttpResponse { String pathElement = pathTokens.nextToken(); // Treat * and ? as wildcard unless they match a literal filename if ((pathElement.contains("?") || pathElement.contains("*")) - && inBase && !root.child((_base.length() > 0 ? _base + "/" : "") + pathElement).exists()) + && inBase && !root.child((!_base.isEmpty() ? _base + "/" : "") + pathElement).exists()) inBase = false; if (pathElement.equals("*zip*")) { // the expected syntax is foo/bar/*zip*/bar.zip @@ -245,7 +241,7 @@ public final class DirectoryBrowserSupport implements HttpResponse { } StringBuilder sb = inBase ? _base : _rest; - if (sb.length() > 0) sb.append('/'); + if (!sb.isEmpty()) sb.append('/'); sb.append(pathElement); if (!inBase) restSize++; @@ -260,13 +256,11 @@ public final class DirectoryBrowserSupport implements HttpResponse { if (base.isEmpty()) { baseFile = root; } else { - if (!SystemProperties.getBoolean(ALLOW_ABSOLUTE_PATH_PROPERTY_NAME, false)) { - boolean isAbsolute = root.run(new IsAbsolute(base)); - if (isAbsolute) { - LOGGER.info(() -> "SECURITY-2481 The path provided in the URL (" + base + ") is absolute and thus is refused."); - rsp.sendError(HttpServletResponse.SC_NOT_FOUND); - return; - } + boolean isAbsolute = root.run(new IsAbsolute(base)); + if (isAbsolute) { + LOGGER.info(() -> "SECURITY-2481 The path provided in the URL (" + base + ") is absolute and thus is refused."); + rsp.sendError(HttpServletResponse.SC_NOT_FOUND); + return; } baseFile = root.child(base); } @@ -315,7 +309,7 @@ public final class DirectoryBrowserSupport implements HttpResponse { } List> glob = null; - boolean patternUsed = rest.length() > 0; + boolean patternUsed = !rest.isEmpty(); boolean containsSymlink = false; boolean containsTmpDir = false; if (patternUsed) { @@ -530,7 +524,7 @@ public final class DirectoryBrowserSupport implements HttpResponse { private static void zip(StaplerResponse rsp, VirtualFile root, VirtualFile dir, String glob) throws IOException, InterruptedException { OutputStream outputStream = rsp.getOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(outputStream)) { - zos.setEncoding(System.getProperty("file.encoding")); // TODO JENKINS-20663 make this overridable via query parameter + zos.setEncoding(Charset.defaultCharset().displayName()); // TODO JENKINS-20663 make this overridable via query parameter // TODO consider using run(Callable) here if (glob.isEmpty()) { diff --git a/core/src/main/java/hudson/model/DownloadService.java b/core/src/main/java/hudson/model/DownloadService.java index b9a5610c4e..0988b36785 100644 --- a/core/src/main/java/hudson/model/DownloadService.java +++ b/core/src/main/java/hudson/model/DownloadService.java @@ -404,7 +404,7 @@ public class DownloadService { } jsonList.add(o); } - if (jsonList.size() == 0 && toolInstallerMetadataExists) { + if (jsonList.isEmpty() && toolInstallerMetadataExists) { return FormValidation.warning("None of the tool installer metadata passed the signature check"); } else if (!toolInstallerMetadataExists) { LOGGER.log(Level.WARNING, "No tool installer metadata found for " + id); diff --git a/core/src/main/java/hudson/model/ExecutorListener.java b/core/src/main/java/hudson/model/ExecutorListener.java index 6500e6acc2..beff34804d 100644 --- a/core/src/main/java/hudson/model/ExecutorListener.java +++ b/core/src/main/java/hudson/model/ExecutorListener.java @@ -30,7 +30,7 @@ import hudson.slaves.SlaveComputer; /** * A listener for task related events from executors. * A {@link Computer#getRetentionStrategy} or {@link SlaveComputer#getLauncher} may implement this interface. - * Or you may create an implementation as an extension (since TODO). + * Or you may create an implementation as an extension (since 2.318). * @author Stephen Connolly * @since 1.312 */ diff --git a/core/src/main/java/hudson/model/Fingerprint.java b/core/src/main/java/hudson/model/Fingerprint.java index bd7721906b..df482e28e0 100644 --- a/core/src/main/java/hudson/model/Fingerprint.java +++ b/core/src/main/java/hudson/model/Fingerprint.java @@ -608,7 +608,7 @@ public class Fingerprint implements ModelObject, Saveable { public synchronized String toString() { StringBuilder buf = new StringBuilder(); for (Range r : ranges) { - if (buf.length() > 0) buf.append(','); + if (!buf.isEmpty()) buf.append(','); buf.append(r); } return buf.toString(); @@ -787,7 +787,7 @@ public class Fingerprint implements ModelObject, Saveable { public static String serialize(RangeSet src) { StringBuilder buf = new StringBuilder(src.ranges.size() * 10); for (Range r : src.ranges) { - if (buf.length() > 0) buf.append(','); + if (!buf.isEmpty()) buf.append(','); if (r.isSingle()) buf.append(r.start); else diff --git a/core/src/main/java/hudson/model/ItemGroupMixIn.java b/core/src/main/java/hudson/model/ItemGroupMixIn.java index 2a6b264db9..faa214dee6 100644 --- a/core/src/main/java/hudson/model/ItemGroupMixIn.java +++ b/core/src/main/java/hudson/model/ItemGroupMixIn.java @@ -290,6 +290,7 @@ public abstract class ItemGroupMixIn { add(result); + result.onCreatedFromScratch(); ItemListener.fireOnCreated(result); Jenkins.get().rebuildDependencyGraphAsync(); diff --git a/core/src/main/java/hudson/model/Items.java b/core/src/main/java/hudson/model/Items.java index a1386565cf..fac4ae8a4a 100644 --- a/core/src/main/java/hudson/model/Items.java +++ b/core/src/main/java/hudson/model/Items.java @@ -208,7 +208,7 @@ public class Items { public static String toNameList(Collection items) { StringBuilder buf = new StringBuilder(); for (Item item : items) { - if (buf.length() > 0) + if (!buf.isEmpty()) buf.append(", "); buf.append(item.getFullName()); } diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java index 92f1d374fb..4bd28ff2e3 100644 --- a/core/src/main/java/hudson/model/Job.java +++ b/core/src/main/java/hudson/model/Job.java @@ -266,8 +266,7 @@ public abstract class Job, RunT extends Run, RunT extends Run entries = new ArrayList<>(); String scmDisplayName = ""; - if (this instanceof SCMTriggerItem) { - SCMTriggerItem scmItem = (SCMTriggerItem) this; + if (this instanceof SCMTriggerItem scmItem) { List scmNames = new ArrayList<>(); for (SCM s : scmItem.getSCMs()) { scmNames.add(s.getDescriptor().getDisplayName()); diff --git a/core/src/main/java/hudson/model/Label.java b/core/src/main/java/hudson/model/Label.java index 94d8b9683e..88661c6df6 100644 --- a/core/src/main/java/hudson/model/Label.java +++ b/core/src/main/java/hudson/model/Label.java @@ -591,7 +591,7 @@ public abstract class Label extends Actionable implements Comparable