package hudson.plugins.throttleconcurrents.pipeline;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.plugins.throttleconcurrents.ThrottleJobProperty;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.steps.BodyExecutionCallback;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ThrottleStepExecution extends StepExecution {
private final ThrottleStep step;
public ThrottleStepExecution(@Nonnull ThrottleStep step, StepContext context) {
super(context);
this.step = step;
}
@Nonnull
public List<String> getCategories() {
return Collections.unmodifiableList(step.getCategories());
}
private List<String> validateCategories(ThrottleJobProperty.DescriptorImpl descriptor, TaskListener listener) {
List<String> undefinedCategories = new ArrayList<>();
Set<String> duplicates = new HashSet<>();
List<String> unique = new ArrayList<>();
if (descriptor.getCategories().isEmpty()) {
undefinedCategories.addAll(getCategories());
} else {
for (String c : getCategories()) {
if (!unique.contains(c)) {
unique.add(c);
} else {
duplicates.add(c);
}
if (descriptor.getCategoryByName(c) == null) {
undefinedCategories.add(c);
}
}
}
if (!duplicates.isEmpty()) {
listener.getLogger().println("One or more duplicate categories (" + StringUtils.join(duplicates, ", ")
+ ") specified. Duplicates will be ignored.");
}
if (!undefinedCategories.isEmpty()) {
throw new IllegalArgumentException("One or more specified categories do not exist: " + StringUtils.join(undefinedCategories, ", "));
}
return unique;
}
@Override
public boolean start() throws Exception {
Run<?, ?> r = getContext().get(Run.class);
TaskListener listener = getContext().get(TaskListener.class);
FlowNode flowNode = getContext().get(FlowNode.class);
ThrottleJobProperty.DescriptorImpl descriptor = ThrottleJobProperty.fetchDescriptor();
String runId = null;
String flowNodeId = null;
if (r != null && flowNode != null) {
runId = r.getExternalizableId();
flowNodeId = flowNode.getId();
for (String category : validateCategories(descriptor, listener)) {
descriptor.addThrottledPipelineForCategory(runId, flowNodeId, category, listener);
}
}
getContext().newBodyInvoker()
.withCallback(new Callback(runId, flowNodeId, getCategories()))
.start();
return false;
}
@Override
public void stop(Throwable cause) throws Exception {
}
private static final class Callback extends BodyExecutionCallback.TailCall {
@CheckForNull
private String runId;
@CheckForNull
private String flowNodeId;
private List<String> categories = new ArrayList<>();
private static final long serialVersionUID = 1;
Callback(@CheckForNull String runId, @CheckForNull String flowNodeId, @Nonnull List<String> categories) {
this.runId = runId;
this.flowNodeId = flowNodeId;
this.categories.addAll(categories);
}
@Override protected void finished(StepContext context) throws Exception {
if (runId != null && flowNodeId != null) {
for (String category : categories) {
ThrottleJobProperty.fetchDescriptor().removeThrottledPipelineForCategory(runId,
flowNodeId,
category,
context.get(TaskListener.class));
}
}
}
}
}