Skip to content

Commit

Permalink
[JENKINS-30269] Step execution implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
amuniz committed Mar 9, 2016
1 parent 85e2b38 commit 5240818
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 11 deletions.
11 changes: 11 additions & 0 deletions pom.xml
Expand Up @@ -59,6 +59,17 @@
<artifactId>workflow-step-api</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>matrix-project</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-annotation</artifactId>
<version>1.14</version>
<optional>true</optional>
</dependency>
</dependencies>

<build>
Expand Down
@@ -1,5 +1,41 @@
package org.jenkins.plugins.lockableresources;

public class LockStep {
import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl;
import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl;
import org.kohsuke.stapler.DataBoundConstructor;

import hudson.Extension;

public class LockStep extends AbstractStepImpl {

public final String resource;

@DataBoundConstructor
public LockStep(String resource) {
if (resource == null || resource.isEmpty()) {
throw new IllegalArgumentException("must specify resource");
}
this.resource = resource;
}

@Extension
public static final class DescriptorImpl extends AbstractStepDescriptorImpl {

public DescriptorImpl() {
super(LockStepExecution.class);
}

@Override public String getFunctionName() {
return "lock";
}

@Override public String getDisplayName() {
return "Lock";
}

@Override public boolean takesImplicitBlockArgument() {
return true;
}
}

}
@@ -0,0 +1,99 @@
package org.jenkins.plugins.lockableresources;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nonnull;

import org.jenkinsci.plugins.workflow.steps.AbstractStepExecutionImpl;
import org.jenkinsci.plugins.workflow.steps.BodyExecution;
import org.jenkinsci.plugins.workflow.steps.BodyExecutionCallback;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepContextParameter;
import org.jenkinsci.plugins.workflow.steps.StepExecution;

import com.google.common.base.Function;
import com.google.inject.Inject;

import hudson.Util;
import hudson.model.Run;
import hudson.model.TaskListener;
import jenkins.util.Timer;

public class LockStepExecution extends AbstractStepExecutionImpl {

@Inject(optional = true)
private transient LockStep step;

@StepContextParameter
private transient Run<?, ?> run;

@StepContextParameter
private transient TaskListener listener;

private transient volatile ScheduledFuture<?> task;

private BodyExecution body;
private final String id = UUID.randomUUID().toString();


@Override
public boolean start() throws Exception {
tryLock();
return false;
}

private void tryLock() {
getContext().saveState();
Timer.get().schedule(new Runnable() {
@Override
public void run() {
LockableResource resource = new LockableResource(step.resource);
if (!proceed()) {
tryLock();
}
}
}, 1000, TimeUnit.MILLISECONDS);
}

private boolean proceed() {
LockableResource resource = new LockableResource(step.resource);
if (LockableResourcesManager.get().lock(Arrays.asList(new LockableResource[] { resource }), run)) {
listener.getLogger().println("Lock aquired on [" + step.resource + "]");
body = getContext().newBodyInvoker().
withCallback(new Callback(resource, run)).
withDisplayName(null).
start();
return true;
} else {
return false;
}
}

private static final class Callback extends BodyExecutionCallback.TailCall {

private final LockableResource resource;
private final Run<?, ?> run;

Callback(LockableResource resource, Run<?, ?> run) {
this.resource = resource;
this.run = run;
}

@Override
protected void finished(StepContext context) throws Exception {
LockableResourcesManager.get().unlock(Arrays.asList(new LockableResource[] { resource }), run);
}

}

@Override
public void stop(Throwable cause) throws Exception {
// TODO does it need implementation?
}

private static final long serialVersionUID = 1L;

}
Expand Up @@ -16,6 +16,7 @@
import hudson.model.AbstractBuild;
import hudson.model.Descriptor;
import hudson.model.Queue;
import hudson.model.Run;
import hudson.model.Queue.Item;
import hudson.model.Queue.Task;
import hudson.model.User;
Expand All @@ -30,9 +31,12 @@
import jenkins.model.Jenkins;

import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;

import com.infradna.tool.bridge_method_injector.WithBridgeMethods;

@ExportedBean(defaultVisibility = 999)
public class LockableResource extends AbstractDescribableImpl<LockableResource> {

Expand All @@ -42,16 +46,16 @@ public class LockableResource extends AbstractDescribableImpl<LockableResource>
public static final String GROOVY_LABEL_MARKER = "groovy:";

private final String name;
private final String description;
private final String labels;
private String reservedBy;
private String description = "";
private String labels = "";
private String reservedBy = null;

private transient int queueItemId = NOT_QUEUED;
private transient String queueItemProject = null;
private transient AbstractBuild<?, ?> build = null;
private transient Run<?, ?> build = null;
private transient long queuingStarted = 0;

@DataBoundConstructor
@Deprecated
public LockableResource(
String name, String description, String labels, String reservedBy) {
this.name = name;
Expand All @@ -60,6 +64,21 @@ public LockableResource(
this.reservedBy = Util.fixEmptyAndTrim(reservedBy);
}

@DataBoundConstructor
public LockableResource(String name) {
this.name = name;
}

@DataBoundSetter
public void setDescription(String description) {
this.description = description;
}

@DataBoundSetter
public void setLabels(String labels) {
this.labels = labels;
}

@Exported
public String getName() {
return name;
Expand Down Expand Up @@ -163,10 +182,19 @@ public boolean isLocked() {
return build != null;
}

public AbstractBuild<?, ?> getBuild() {
@WithBridgeMethods(value=AbstractBuild.class, adapterMethod="getAbstractBuild")
public Run<?, ?> getBuild() {
return build;
}

/**
* @see {@link WithBridgeMethods}
*/
@Deprecated
private Object getAbstractBuild(final Run owner, final Class targetClass) {
return owner instanceof AbstractBuild ? (AbstractBuild) owner : null;
}

@Exported
public String getBuildName() {
if (build != null)
Expand All @@ -175,7 +203,7 @@ public String getBuildName() {
return null;
}

public void setBuild(AbstractBuild<?, ?> lockedBy) {
public void setBuild(Run<?, ?> lockedBy) {
this.build = lockedBy;
}

Expand Down Expand Up @@ -216,6 +244,7 @@ private void validateQueuingTimeout() {
}
}

@DataBoundSetter
public void setReservedBy(String userName) {
this.reservedBy = userName;
}
Expand Down
Expand Up @@ -10,6 +10,7 @@

import hudson.Extension;
import hudson.model.AbstractBuild;
import hudson.model.Run;

import java.util.HashSet;
import java.util.Map;
Expand Down Expand Up @@ -60,7 +61,7 @@ public List<LockableResource> getResourcesFromProject(String fullName) {
public List<LockableResource> getResourcesFromBuild(AbstractBuild<?, ?> build) {
List<LockableResource> matching = new ArrayList<LockableResource>();
for (LockableResource r : resources) {
AbstractBuild<?, ?> rBuild = r.getBuild();
Run<?, ?> rBuild = r.getBuild();
if (rBuild != null && rBuild == build) {
matching.add(r);
}
Expand Down Expand Up @@ -206,7 +207,7 @@ private boolean checkCurrentResourcesStatus(List<LockableResource> selected,
}

public synchronized boolean lock(List<LockableResource> resources,
AbstractBuild<?, ?> build) {
Run<?, ?> build) {
for (LockableResource r : resources) {
if (r.isReserved() || r.isLocked()) {
return false;
Expand All @@ -220,7 +221,7 @@ public synchronized boolean lock(List<LockableResource> resources,
}

public synchronized void unlock(List<LockableResource> resources,
AbstractBuild<?, ?> build) {
Run<?, ?> build) {
for (LockableResource r : resources) {
if (build == null || build == r.getBuild()) {
r.unqueue();
Expand Down

0 comments on commit 5240818

Please sign in to comment.