@@ -30,6 +30,7 @@
import org .jenkinsci .plugins .workflow .flow .FlowExecutionList ;
import com .google .common .util .concurrent .ListenableFuture ;
import com .google .common .util .concurrent .SettableFuture ;
import edu .umd .cs .findbugs .annotations .SuppressFBWarnings ;
import hudson .AbortException ;
import hudson .Extension ;
import hudson .FilePath ;
@@ -48,6 +49,7 @@
import hudson .scm .SCM ;
import hudson .scm .SCMRevisionState ;
import hudson .util .NullStream ;
import hudson .util .PersistedList ;
import java .io .File ;
import java .io .FileOutputStream ;
import java .io .IOException ;
@@ -58,7 +60,6 @@
import java .util .HashMap ;
import java .util .Iterator ;
import java .util .LinkedHashMap ;
import java .util .LinkedList ;
import java .util .List ;
import java .util .Map ;
import java .util .concurrent .Future ;
@@ -87,7 +88,7 @@
import org .kohsuke .stapler .interceptor .RequirePOST ;
@ SuppressWarnings ("SynchronizeOnNonFinalField" )
@ edu . umd . cs . findbugs . annotations . SuppressWarnings ( "JLM_JSR166_UTILCONCURRENT_MONITORENTER" ) // completed is an unusual usage
@ SuppressFBWarnings ( value = "JLM_JSR166_UTILCONCURRENT_MONITORENTER" , justification = " completed is an unusual usage" )
public final class WorkflowRun extends Run <WorkflowJob ,WorkflowRun > implements Queue .Executable , LazyBuildMixIn .LazyLoadingRun <WorkflowJob ,WorkflowRun > {
private static final Logger LOGGER = Logger .getLogger (WorkflowRun .class .getName ());
@@ -112,12 +113,14 @@ public final class WorkflowRun extends Run<WorkflowJob,WorkflowRun> implements Q
/** map from node IDs to log positions from which we should copy text */
private Map <String ,Long > logsToCopy ;
List <SCMCheckout > checkouts ;
/** JENKINS-26761: supposed to always be set but sometimes is not. Access only through {@link #checkouts(TaskListener)}. */
private @ CheckForNull List <SCMCheckout > checkouts ;
// TODO could use a WeakReference to reduce memory, but that complicates how we add to it incrementally; perhaps keep a List<WeakReference<ChangeLogSet<?>>>
private transient List <ChangeLogSet <? extends ChangeLogSet .Entry >> changeSets ;
public WorkflowRun (WorkflowJob job ) throws IOException {
super (job );
checkouts = new PersistedList <SCMCheckout >(this );
//System.err.printf("created %s @%h%n", this, this);
}
@@ -165,7 +168,6 @@ public WorkflowRun(WorkflowJob job, File dir) throws IOException {
listener .error ("No flow definition, cannot run" );
return ;
}
checkouts = new LinkedList <SCMCheckout >();
Owner owner = new Owner (this );
execution = definition .create (owner , listener , getAllActions ());
FlowExecutionList .get ().register (owner );
@@ -366,6 +368,7 @@ private String key() {
Queue .getInstance ().schedule (new AfterRestartTask (this ), 0 );
}
}
checkouts (null ); // only for diagnostics
synchronized (LOADING_RUNS ) {
LOADING_RUNS .remove (key ()); // or could just make the value type be WeakReference<WorkflowRun>
LOADING_RUNS .notifyAll ();
@@ -446,15 +449,23 @@ public boolean hasntStartedYet() {
return isBuilding (); // there is no equivalent to a post-production state for flows
}
synchronized @ Nonnull List <SCMCheckout > checkouts (@ CheckForNull TaskListener listener ) {
if (checkouts == null ) {
LOGGER .log (Level .WARNING , "JENKINS-26761: no checkouts in {0}" , this );
if (listener != null ) {
listener .error ("JENKINS-26761: list of SCM checkouts in " + this + " was lost; polling will be broken" );
}
checkouts = new PersistedList <SCMCheckout >(this );
// Could this.save(), but might pollute diagnosis, and (worse) might clobber real data if there is >1 WorkflowRun with the same ID.
}
return checkouts ;
}
@ Exported
public synchronized List <ChangeLogSet <? extends ChangeLogSet .Entry >> getChangeSets () {
if (changeSets == null ) {
changeSets = new ArrayList <ChangeLogSet <? extends ChangeLogSet .Entry >>();
if (checkouts == null ) {
LOGGER .log (Level .WARNING , "no checkouts in {0}" , this );
return changeSets ;
}
for (SCMCheckout co : checkouts ) {
for (SCMCheckout co : checkouts (null )) {
if (co .changelogFile != null && co .changelogFile .isFile ()) {
try {
changeSets .add (co .scm .createChangeLogParser ().parse (this , co .scm .getEffectiveBrowser (), co .changelogFile ));
@@ -497,7 +508,7 @@ public synchronized HttpResponse doStop() {
return null ;
}
private void onCheckout (SCM scm , FilePath workspace , @ CheckForNull File changelogFile , @ CheckForNull SCMRevisionState pollingBaseline ) throws Exception {
private void onCheckout (SCM scm , FilePath workspace , TaskListener listener , @ CheckForNull File changelogFile , @ CheckForNull SCMRevisionState pollingBaseline ) throws Exception {
if (changelogFile != null && changelogFile .isFile ()) {
ChangeLogSet <?> cls = scm .createChangeLogParser ().parse (this , scm .getEffectiveBrowser (), changelogFile );
getChangeSets ().add (cls );
@@ -519,7 +530,7 @@ private void onCheckout(SCM scm, FilePath workspace, @CheckForNull File changelo
if (node == null ) {
throw new IllegalStateException ();
}
checkouts .add (new SCMCheckout (scm , node , workspace .getRemote (), changelogFile , pollingBaseline ));
checkouts ( listener ) .add (new SCMCheckout (scm , node , workspace .getRemote (), changelogFile , pollingBaseline ));
}
static final class SCMCheckout {
@@ -669,7 +680,7 @@ static void alias() {
@ Extension public static final class SCMListenerImpl extends SCMListener {
@ Override public void onCheckout (Run <?,?> build , SCM scm , FilePath workspace , TaskListener listener , File changelogFile , SCMRevisionState pollingBaseline ) throws Exception {
if (build instanceof WorkflowRun ) {
((WorkflowRun ) build ).onCheckout (scm , workspace , changelogFile , pollingBaseline );
((WorkflowRun ) build ).onCheckout (scm , workspace , listener , changelogFile , pollingBaseline );
}
}
}