From 8fbe0d39defc021dda6bf173280476dc6258a490 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:07:37 -0800 Subject: [PATCH 1/4] Bump com.puppycrawl.tools:checkstyle from 10.12.5 to 10.12.6 (#8760) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d5e48d82d159..eef7b86e372c 100644 --- a/pom.xml +++ b/pom.xml @@ -332,7 +332,7 @@ THE SOFTWARE. com.puppycrawl.tools checkstyle - 10.12.5 + 10.12.6 From 6f8c3fe260bc9f3f534c1803c033aef1322738d9 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 8 Dec 2023 07:45:47 -0500 Subject: [PATCH 2/4] Avoid `CLICommandInvoker.authorizedTo` in `QuietDownCommandTest` (#8754) --- .../java/hudson/cli/QuietDownCommandTest.java | 46 +++++++++++-------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/test/src/test/java/hudson/cli/QuietDownCommandTest.java b/test/src/test/java/hudson/cli/QuietDownCommandTest.java index 2e57ebcf8131..60932dd1e1ff 100644 --- a/test/src/test/java/hudson/cli/QuietDownCommandTest.java +++ b/test/src/test/java/hudson/cli/QuietDownCommandTest.java @@ -55,6 +55,7 @@ import org.jvnet.hudson.test.BuildWatcher; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.LoggerRule; +import org.jvnet.hudson.test.MockAuthorizationStrategy; /** * @author pjanouse @@ -66,6 +67,9 @@ public class QuietDownCommandTest { = new QueueTest.TestFlyweightTask(new AtomicInteger(), null); private static final String TEST_REASON = "test reason"; + private static final String VIEWER = "viewer"; + private static final String ADMIN = "admin"; + @ClassRule public static final BuildWatcher buildWatcher = new BuildWatcher(); @@ -78,22 +82,26 @@ public class QuietDownCommandTest { @Before public void setUp() { command = new CLICommandInvoker(j, "quiet-down"); + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy(). + grant(Jenkins.ADMINISTER).everywhere().to(ADMIN). + grant(Jenkins.READ).everywhere().to(VIEWER)); } @Test public void quietDownShouldFailWithoutAdministerPermission() { final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ) + .asUser(VIEWER) .invoke(); assertThat(result, failedWith(6)); assertThat(result, hasNoStandardOutput()); - assertThat(result.stderr(), containsString("ERROR: user is missing the Overall/Administer permission")); + assertThat(result.stderr(), containsString("ERROR: " + VIEWER + " is missing the Overall/Administer permission")); } @Test public void quietDownShouldSuccess() { final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invoke(); assertThat(result, succeededSilently()); assertJenkinsInQuietMode(); @@ -102,7 +110,7 @@ public void quietDownShouldSuccess() { @Test public void quietDownShouldSuccessWithBlock() { final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-block"); assertThat(result, succeededSilently()); assertJenkinsInQuietMode(); @@ -111,7 +119,7 @@ public void quietDownShouldSuccessWithBlock() { @Test public void quietDownShouldSuccessWithTimeout() { final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-timeout", "0"); assertThat(result, succeededSilently()); assertJenkinsInQuietMode(); @@ -120,7 +128,7 @@ public void quietDownShouldSuccessWithTimeout() { @Test public void quietDownShouldSuccessWithReason() { final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-reason", TEST_REASON); assertThat(result, succeededSilently()); assertJenkinsInQuietMode(); @@ -130,7 +138,7 @@ public void quietDownShouldSuccessWithReason() { @Test public void quietDownShouldSuccessWithBlockAndTimeout() { final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-block", "-timeout", "0"); assertThat(result, succeededSilently()); assertJenkinsInQuietMode(); @@ -139,7 +147,7 @@ public void quietDownShouldSuccessWithBlockAndTimeout() { @Test public void quietDownShouldSuccessWithBlockAndTimeoutAndReason() { final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-block", "-timeout", "0", "-reason", TEST_REASON); assertThat(result, succeededSilently()); assertJenkinsInQuietMode(); @@ -149,7 +157,7 @@ public void quietDownShouldSuccessWithBlockAndTimeoutAndReason() { @Test public void quietDownShouldFailWithEmptyTimeout() { final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-timeout"); assertThat(result, failedWith(2)); assertThat(result, hasNoStandardOutput()); @@ -161,7 +169,7 @@ public void quietDownShouldSuccessOnAlreadyQuietDownedJenkins() { j.jenkins.doQuietDown(); assertJenkinsInQuietMode(); final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invoke(); assertThat(result, succeededSilently()); assertJenkinsInQuietMode(); @@ -172,7 +180,7 @@ public void quietDownShouldSuccessWithBlockOnAlreadyQuietDownedJenkins() throws j.jenkins.doQuietDown(true, 0, null, false); assertJenkinsInQuietMode(); final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-block"); assertThat(result, succeededSilently()); assertJenkinsInQuietMode(); @@ -184,7 +192,7 @@ public void quietDownShouldSuccessWithBlockAndTimeoutOnAlreadyQuietDownedJenkins assertJenkinsInQuietMode(); final long time_before = System.currentTimeMillis(); final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-block", "-timeout", "20000"); assertThat(result, succeededSilently()); assertThat(System.currentTimeMillis() < time_before + 20000, equalTo(true)); @@ -202,7 +210,7 @@ public void quietDownShouldSuccessAndRunningExecutor() throws Exception { final FreeStyleBuild build = OnlineNodeCommandTest.startBlockingAndFinishingBuild(project, finish); final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invoke(); assertThat(result, succeededSilently()); assertJenkinsInQuietMode(); @@ -230,7 +238,7 @@ public void quietDownShouldSuccessWithBlockAndRunningExecutor() throws Exception assertJenkinsNotInQuietMode(); beforeCli.signal(); final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-block"); fail("Should never return from previous CLI call!"); return null; @@ -271,7 +279,7 @@ public void quietDownShouldSuccessWithBlockAndZeroTimeoutAndRunningExecutor() th assertJenkinsNotInQuietMode(); beforeCli.signal(); final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-block", "-timeout", "0"); fail("Should never return from previous CLI call!"); return null; @@ -313,7 +321,7 @@ public void quietDownShouldSuccessWithBlockPlusExpiredTimeoutAndRunningExecutor( final long time_before = System.currentTimeMillis(); beforeCli.signal(); final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-block", "-timeout", Integer.toString(TIMEOUT)); assertThat(result, succeededSilently()); assertThat(System.currentTimeMillis() > time_before + TIMEOUT, equalTo(true)); @@ -353,7 +361,7 @@ public void quietDownShouldSuccessWithBlockPlusNonExpiredTimeoutAndRunningExecut assertJenkinsNotInQuietMode(); beforeCli.signal(); final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-block", "-timeout", Integer.toString(2 * TIMEOUT)); fail("Blocking call shouldn't finish, should be killed by called thread!"); return null; @@ -397,7 +405,7 @@ public void quietDownShouldSuccessWithBlockAndFinishingExecutor() throws Excepti final long time_before = System.currentTimeMillis(); beforeCli.signal(); final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-block"); assertThat(result, succeededSilently()); assertThat(System.currentTimeMillis() > time_before + 1000, equalTo(true)); @@ -437,7 +445,7 @@ public void quietDownShouldSuccessWithBlockAndNonExpiredTimeoutAndFinishingExecu final long time_before = System.currentTimeMillis(); beforeCli.signal(); final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ, Jenkins.ADMINISTER) + .asUser(ADMIN) .invokeWithArgs("-block", "-timeout", Integer.toString(TIMEOUT)); assertThat(result, succeededSilently()); assertThat(System.currentTimeMillis() > time_before + 1000, equalTo(true)); From 85c1f8ddf228b1def6a8251b8d13512209552b2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:13:54 -0800 Subject: [PATCH 3/4] Bump org.jenkins-ci.plugins:credentials from 1309.v8835d63eb_d8a_ to 1311.vcf0a_900b_37c2 (#8764) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- test/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pom.xml b/test/pom.xml index ffe5e884de35..0ffbfe5a1469 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -206,7 +206,7 @@ THE SOFTWARE. org.jenkins-ci.plugins credentials - 1309.v8835d63eb_d8a_ + 1311.vcf0a_900b_37c2 test From f9a777bc682963de4640303b2f28ac488f9b93ef Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Sat, 9 Dec 2023 04:25:15 -0500 Subject: [PATCH 4/4] Class loading deadlock between `PermalinkProjectAction.Permalink` & `PeepholePermalink` (#8736) * Class loading deadlock between `PermalinkProjectAction` & `PeepholePermalink` * Checkstyle * Clearer reproducer * Do not let `Permalink` refer to its subclass `PeepholePermalink` in its static initializer * Cleaner test * Checkstyle * Maybe we should not run `initialized` from `Job.` either --- core/src/main/java/hudson/model/Job.java | 14 +- .../hudson/model/PermalinkProjectAction.java | 118 +++------------ .../java/jenkins/model/PeepholePermalink.java | 143 ++++++++++++++++++ .../jenkins/model/PeepholePermalinkTest.java | 65 ++++++++ 4 files changed, 234 insertions(+), 106 deletions(-) create mode 100644 core/src/test/java/jenkins/model/PeepholePermalinkTest.java diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java index c40eee681de3..ab32752704be 100644 --- a/core/src/main/java/hudson/model/Job.java +++ b/core/src/main/java/hudson/model/Job.java @@ -92,6 +92,7 @@ import jenkins.model.Jenkins; import jenkins.model.JenkinsLocationConfiguration; import jenkins.model.ModelObjectWithChildren; +import jenkins.model.PeepholePermalink; import jenkins.model.ProjectNamingStrategy; import jenkins.model.RunIdMigrator; import jenkins.model.lazy.LazyBuildMixIn; @@ -941,7 +942,7 @@ public RunT getFirstBuild() { @Exported @QuickSilver public RunT getLastSuccessfulBuild() { - return (RunT) Permalink.LAST_SUCCESSFUL_BUILD.resolve(this); + return (RunT) PeepholePermalink.LAST_SUCCESSFUL_BUILD.resolve(this); } /** @@ -951,7 +952,7 @@ public RunT getLastSuccessfulBuild() { @Exported @QuickSilver public RunT getLastUnsuccessfulBuild() { - return (RunT) Permalink.LAST_UNSUCCESSFUL_BUILD.resolve(this); + return (RunT) PeepholePermalink.LAST_UNSUCCESSFUL_BUILD.resolve(this); } /** @@ -961,7 +962,7 @@ public RunT getLastUnsuccessfulBuild() { @Exported @QuickSilver public RunT getLastUnstableBuild() { - return (RunT) Permalink.LAST_UNSTABLE_BUILD.resolve(this); + return (RunT) PeepholePermalink.LAST_UNSTABLE_BUILD.resolve(this); } /** @@ -971,7 +972,7 @@ public RunT getLastUnstableBuild() { @Exported @QuickSilver public RunT getLastStableBuild() { - return (RunT) Permalink.LAST_STABLE_BUILD.resolve(this); + return (RunT) PeepholePermalink.LAST_STABLE_BUILD.resolve(this); } /** @@ -980,7 +981,7 @@ public RunT getLastStableBuild() { @Exported @QuickSilver public RunT getLastFailedBuild() { - return (RunT) Permalink.LAST_FAILED_BUILD.resolve(this); + return (RunT) PeepholePermalink.LAST_FAILED_BUILD.resolve(this); } /** @@ -989,7 +990,7 @@ public RunT getLastFailedBuild() { @Exported @QuickSilver public RunT getLastCompletedBuild() { - return (RunT) Permalink.LAST_COMPLETED_BUILD.resolve(this); + return (RunT) PeepholePermalink.LAST_COMPLETED_BUILD.resolve(this); } /** @@ -1067,6 +1068,7 @@ public long getEstimatedDuration() { * @return never null */ public PermalinkList getPermalinks() { + PeepholePermalink.initialized(); // TODO: shall we cache this? PermalinkList permalinks = new PermalinkList(Permalink.BUILTIN); for (PermalinkProjectAction ppa : getActions(PermalinkProjectAction.class)) { diff --git a/core/src/main/java/hudson/model/PermalinkProjectAction.java b/core/src/main/java/hudson/model/PermalinkProjectAction.java index 9013ddd5efc5..4ab5478243c3 100644 --- a/core/src/main/java/hudson/model/PermalinkProjectAction.java +++ b/core/src/main/java/hudson/model/PermalinkProjectAction.java @@ -25,7 +25,6 @@ package hudson.model; import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import jenkins.model.PeepholePermalink; @@ -111,114 +110,33 @@ public String getId() { return job.getLastBuild(); } }; - public static final Permalink LAST_STABLE_BUILD = new PeepholePermalink() { - @Override - public String getDisplayName() { - return Messages.Permalink_LastStableBuild(); - } - - @Override - public String getId() { - return "lastStableBuild"; - } - - @Override - public boolean apply(Run run) { - return !run.isBuilding() && run.getResult() == Result.SUCCESS; - } - }; - public static final Permalink LAST_SUCCESSFUL_BUILD = new PeepholePermalink() { - @Override - public String getDisplayName() { - return Messages.Permalink_LastSuccessfulBuild(); - } - - @Override - public String getId() { - return "lastSuccessfulBuild"; - } - - @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "TODO needs triage") - @Override - public boolean apply(Run run) { - return !run.isBuilding() && run.getResult().isBetterOrEqualTo(Result.UNSTABLE); - } - }; - public static final Permalink LAST_FAILED_BUILD = new PeepholePermalink() { - @Override - public String getDisplayName() { - return Messages.Permalink_LastFailedBuild(); - } - - @Override - public String getId() { - return "lastFailedBuild"; - } - - @Override - public boolean apply(Run run) { - return !run.isBuilding() && run.getResult() == Result.FAILURE; - } - }; - public static final Permalink LAST_UNSTABLE_BUILD = new PeepholePermalink() { - @Override - public String getDisplayName() { - return Messages.Permalink_LastUnstableBuild(); - } + /** @deprecated use {@link PeepholePermalink#LAST_STABLE_BUILD} */ + @Deprecated + public static Permalink LAST_STABLE_BUILD; - @Override - public String getId() { - return "lastUnstableBuild"; - } + /** @deprecated use {@link PeepholePermalink#LAST_SUCCESSFUL_BUILD} */ + @Deprecated + public static Permalink LAST_SUCCESSFUL_BUILD; - @Override - public boolean apply(Run run) { - return !run.isBuilding() && run.getResult() == Result.UNSTABLE; - } - }; + /** @deprecated use {@link PeepholePermalink#LAST_FAILED_BUILD} */ + @Deprecated + public static Permalink LAST_FAILED_BUILD; - public static final Permalink LAST_UNSUCCESSFUL_BUILD = new PeepholePermalink() { - @Override - public String getDisplayName() { - return Messages.Permalink_LastUnsuccessfulBuild(); - } + /** @deprecated use {@link PeepholePermalink#LAST_UNSTABLE_BUILD} */ + @Deprecated + public static Permalink LAST_UNSTABLE_BUILD; - @Override - public String getId() { - return "lastUnsuccessfulBuild"; - } - - @Override - public boolean apply(Run run) { - return !run.isBuilding() && run.getResult() != Result.SUCCESS; - } - }; - public static final Permalink LAST_COMPLETED_BUILD = new PeepholePermalink() { - @Override - public String getDisplayName() { - return Messages.Permalink_LastCompletedBuild(); - } + /** @deprecated use {@link PeepholePermalink#LAST_UNSUCCESSFUL_BUILD} */ + @Deprecated + public static Permalink LAST_UNSUCCESSFUL_BUILD; - @Override - public String getId() { - return "lastCompletedBuild"; - } - - @Override - public boolean apply(Run run) { - return !run.isBuilding(); - } - }; + /** @deprecated use {@link PeepholePermalink#LAST_COMPLETED_BUILD} */ + @Deprecated + public static Permalink LAST_COMPLETED_BUILD; static { BUILTIN.add(LAST_BUILD); - BUILTIN.add(LAST_STABLE_BUILD); - BUILTIN.add(LAST_SUCCESSFUL_BUILD); - BUILTIN.add(LAST_FAILED_BUILD); - BUILTIN.add(LAST_UNSTABLE_BUILD); - BUILTIN.add(LAST_UNSUCCESSFUL_BUILD); - BUILTIN.add(LAST_COMPLETED_BUILD); } } } diff --git a/core/src/main/java/jenkins/model/PeepholePermalink.java b/core/src/main/java/jenkins/model/PeepholePermalink.java index 256dc438d4ae..40297b6e406c 100644 --- a/core/src/main/java/jenkins/model/PeepholePermalink.java +++ b/core/src/main/java/jenkins/model/PeepholePermalink.java @@ -2,10 +2,12 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.Util; import hudson.model.Job; import hudson.model.PermalinkProjectAction.Permalink; +import hudson.model.Result; import hudson.model.Run; import hudson.model.TaskListener; import hudson.model.listeners.RunListener; @@ -21,6 +23,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; /** * Convenient base implementation for {@link Permalink}s that satisfy @@ -237,6 +241,145 @@ public void onCompleted(Run run, @NonNull TaskListener listener) { } } + /** + * @since TODO + */ + public static final Permalink LAST_STABLE_BUILD = new PeepholePermalink() { + @Override + public String getDisplayName() { + return hudson.model.Messages.Permalink_LastStableBuild(); + } + + @Override + public String getId() { + return "lastStableBuild"; + } + + @Override + public boolean apply(Run run) { + return !run.isBuilding() && run.getResult() == Result.SUCCESS; + } + }; + + /** + * @since TODO + */ + public static final Permalink LAST_SUCCESSFUL_BUILD = new PeepholePermalink() { + @Override + public String getDisplayName() { + return hudson.model.Messages.Permalink_LastSuccessfulBuild(); + } + + @Override + public String getId() { + return "lastSuccessfulBuild"; + } + + @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "TODO needs triage") + @Override + public boolean apply(Run run) { + return !run.isBuilding() && run.getResult().isBetterOrEqualTo(Result.UNSTABLE); + } + }; + + /** + * @since TODO + */ + public static final Permalink LAST_FAILED_BUILD = new PeepholePermalink() { + @Override + public String getDisplayName() { + return hudson.model.Messages.Permalink_LastFailedBuild(); + } + + @Override + public String getId() { + return "lastFailedBuild"; + } + + @Override + public boolean apply(Run run) { + return !run.isBuilding() && run.getResult() == Result.FAILURE; + } + }; + + /** + * @since TODO + */ + public static final Permalink LAST_UNSTABLE_BUILD = new PeepholePermalink() { + @Override + public String getDisplayName() { + return hudson.model.Messages.Permalink_LastUnstableBuild(); + } + + @Override + public String getId() { + return "lastUnstableBuild"; + } + + @Override + public boolean apply(Run run) { + return !run.isBuilding() && run.getResult() == Result.UNSTABLE; + } + }; + + /** + * @since TODO + */ + public static final Permalink LAST_UNSUCCESSFUL_BUILD = new PeepholePermalink() { + @Override + public String getDisplayName() { + return hudson.model.Messages.Permalink_LastUnsuccessfulBuild(); + } + + @Override + public String getId() { + return "lastUnsuccessfulBuild"; + } + + @Override + public boolean apply(Run run) { + return !run.isBuilding() && run.getResult() != Result.SUCCESS; + } + }; + + /** + * @since TODO + */ + public static final Permalink LAST_COMPLETED_BUILD = new PeepholePermalink() { + @Override + public String getDisplayName() { + return hudson.model.Messages.Permalink_LastCompletedBuild(); + } + + @Override + public String getId() { + return "lastCompletedBuild"; + } + + @Override + public boolean apply(Run run) { + return !run.isBuilding(); + } + }; + + static { + BUILTIN.add(LAST_STABLE_BUILD); + BUILTIN.add(LAST_SUCCESSFUL_BUILD); + BUILTIN.add(LAST_FAILED_BUILD); + BUILTIN.add(LAST_UNSTABLE_BUILD); + BUILTIN.add(LAST_UNSUCCESSFUL_BUILD); + BUILTIN.add(LAST_COMPLETED_BUILD); + Permalink.LAST_STABLE_BUILD = LAST_STABLE_BUILD; + Permalink.LAST_SUCCESSFUL_BUILD = LAST_SUCCESSFUL_BUILD; + Permalink.LAST_FAILED_BUILD = LAST_FAILED_BUILD; + Permalink.LAST_UNSTABLE_BUILD = LAST_UNSTABLE_BUILD; + Permalink.LAST_UNSUCCESSFUL_BUILD = LAST_UNSUCCESSFUL_BUILD; + Permalink.LAST_COMPLETED_BUILD = LAST_COMPLETED_BUILD; + } + + @Restricted(NoExternalUse.class) + public static void initialized() {} + private static final int RESOLVES_TO_NONE = -1; private static final Logger LOGGER = Logger.getLogger(PeepholePermalink.class.getName()); diff --git a/core/src/test/java/jenkins/model/PeepholePermalinkTest.java b/core/src/test/java/jenkins/model/PeepholePermalinkTest.java new file mode 100644 index 000000000000..8a2cb059b230 --- /dev/null +++ b/core/src/test/java/jenkins/model/PeepholePermalinkTest.java @@ -0,0 +1,65 @@ +/* + * The MIT License + * + * Copyright 2023 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; + +import hudson.model.PermalinkProjectAction; +import hudson.model.Run; +import java.util.stream.Collectors; +import org.junit.Test; + +public final class PeepholePermalinkTest { + + @Test + public void classLoadingDeadlock() throws Exception { + PeepholePermalink.initialized(); + Thread t = new Thread(() -> { + assertThat("successfully loaded permalinks", + PermalinkProjectAction.Permalink.BUILTIN.stream().map(p -> p.getId()).collect(Collectors.toSet()), + containsInAnyOrder("lastBuild", "lastStableBuild", "lastSuccessfulBuild", "lastFailedBuild", "lastUnstableBuild", "lastUnsuccessfulBuild", "lastCompletedBuild")); + }); + t.start(); + new PeepholePermalink() { + @Override + public boolean apply(Run run) { + throw new UnsupportedOperationException(); + } + + @Override + public String getDisplayName() { + throw new UnsupportedOperationException(); + } + + @Override + public String getId() { + throw new UnsupportedOperationException(); + } + }; + t.join(); + } + +}