diff --git a/src/main/java/org/jenkinsci/plugins/workflow/steps/AbstractSynchronousNonBlockingStepExecution.java b/src/main/java/org/jenkinsci/plugins/workflow/steps/AbstractSynchronousNonBlockingStepExecution.java index 26d200a..ae58702 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/steps/AbstractSynchronousNonBlockingStepExecution.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/steps/AbstractSynchronousNonBlockingStepExecution.java @@ -67,7 +67,8 @@ public void stop(Throwable cause) throws Exception { @Override public void onResume() { - getContext().onFailure(new SynchronousResumeNotSupportedException()); + var context = getContext(); + context.onFailure(new SynchronousResumeNotSupportedException(context)); } @Override public @NonNull String getStatus() { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/steps/GeneralNonBlockingStepExecution.java b/src/main/java/org/jenkinsci/plugins/workflow/steps/GeneralNonBlockingStepExecution.java index 98f7e8a..a83334e 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/steps/GeneralNonBlockingStepExecution.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/steps/GeneralNonBlockingStepExecution.java @@ -104,7 +104,8 @@ public void stop(Throwable cause) throws Exception { @Override public void onResume() { if (threadName != null) { - getContext().onFailure(new SynchronousResumeNotSupportedException()); + var context = getContext(); + context.onFailure(new SynchronousResumeNotSupportedException(context)); } } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/steps/SynchronousNonBlockingStepExecution.java b/src/main/java/org/jenkinsci/plugins/workflow/steps/SynchronousNonBlockingStepExecution.java index 6867490..acfc0d3 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/steps/SynchronousNonBlockingStepExecution.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/steps/SynchronousNonBlockingStepExecution.java @@ -5,6 +5,8 @@ import hudson.util.ClassLoaderSanityThreadFactory; import hudson.util.DaemonThreadFactory; import hudson.util.NamingThreadFactory; + +import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -72,7 +74,8 @@ public void stop(Throwable cause) throws Exception { @Override public void onResume() { - getContext().onFailure(new SynchronousResumeNotSupportedException()); + var context = getContext(); + context.onFailure(new SynchronousResumeNotSupportedException(context)); } @Override public @NonNull String getStatus() { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/steps/SynchronousResumeNotSupportedException.java b/src/main/java/org/jenkinsci/plugins/workflow/steps/SynchronousResumeNotSupportedException.java index 2231c55..5181e5a 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/steps/SynchronousResumeNotSupportedException.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/steps/SynchronousResumeNotSupportedException.java @@ -24,6 +24,10 @@ package org.jenkinsci.plugins.workflow.steps; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + /** * May be reported from {@link StepExecution#onResume} when the step does not support resumption. * Thrown by default from {@link SynchronousNonBlockingStepExecution}, @@ -33,8 +37,32 @@ */ public class SynchronousResumeNotSupportedException extends Exception { + private static final Logger LOGGER = Logger.getLogger(SynchronousResumeNotSupportedException.class.getName()); + + /** + * @deprecated Use {@link #SynchronousResumeNotSupportedException(StepContext)} instead. + */ + @Deprecated public SynchronousResumeNotSupportedException() { super("Resume after a restart not supported for non-blocking synchronous steps"); } + public SynchronousResumeNotSupportedException(StepContext context) { + super(String.format(""" + The Pipeline step `%s` cannot be resumed after a controller restart. \ + In Scripted syntax, you may wrap its containing `node` block within `retry(conditions: [nonresumable()], count: 2) {...}`, \ + or, in Declarative syntax, use the `retries` option to an `agent` directive to allow the stage to be retried.""", + getStepDisplayFunctionName(context)) + ); + } + + private static String getStepDisplayFunctionName(StepContext stepContext) { + StepDescriptor descriptor = null; + try { + descriptor = stepContext.get(StepDescriptor.class); + } catch (IOException | InterruptedException e) { + LOGGER.log(Level.FINE, "Failed to get descriptor: ", e); + } + return descriptor != null ? descriptor.getFunctionName() : "?"; + } }