Skip to content

Commit

Permalink
Class loading deadlock between PermalinkProjectAction.Permalink & `…
Browse files Browse the repository at this point in the history
…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.<clinit>` either
  • Loading branch information
jglick committed Dec 9, 2023
1 parent 85c1f8d commit f9a777b
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 106 deletions.
14 changes: 8 additions & 6 deletions core/src/main/java/hudson/model/Job.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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)) {
Expand Down
118 changes: 18 additions & 100 deletions core/src/main/java/hudson/model/PermalinkProjectAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
}
143 changes: 143 additions & 0 deletions core/src/main/java/jenkins/model/PeepholePermalink.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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();

Check warning on line 291 in core/src/main/java/jenkins/model/PeepholePermalink.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 291 is not covered by tests
}

@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();

Check warning on line 311 in core/src/main/java/jenkins/model/PeepholePermalink.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 311 is not covered by tests
}

@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();

Check warning on line 331 in core/src/main/java/jenkins/model/PeepholePermalink.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 331 is not covered by tests
}

@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());
Expand Down
Loading

0 comments on commit f9a777b

Please sign in to comment.