package hudson.plugins.batch_task; import hudson.model.AbstractBuild; import hudson.model.AbstractModelObject; import hudson.model.AbstractProject; import hudson.model.BallColor; import hudson.model.Cause.UserCause; import hudson.model.CauseAction; import hudson.model.Hudson; import hudson.model.Label; import hudson.model.Node; import hudson.model.Queue; import hudson.model.ResourceList; import hudson.model.Result; import hudson.model.Job; import hudson.model.queue.CauseOfBlockage; import hudson.util.Iterators; import hudson.widgets.BuildHistoryWidget; import hudson.widgets.HistoryWidget; import hudson.widgets.HistoryWidget.Adapter; import hudson.security.ACL; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import javax.servlet.ServletException; import java.io.IOException; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; /** * A batch task. * * @author Kohsuke Kawaguchi */ public final class BatchTask extends AbstractModelObject implements Queue.Task { /** * Name of this task. Used for display. */ public final String name; /** * Shell script to be executed. */ public final String script; /*package*/ transient AbstractProject<?,?> owner; /*package*/ transient BatchTaskProperty parent; @DataBoundConstructor public BatchTask(String name, String script) { this.name = name.trim(); this.script = script; } public BatchTaskProperty getParent() { return parent; } public String getSearchUrl() { return name; } public String getDisplayName() { return name; } public String getFullDisplayName() { return owner.getFullDisplayName()+" \u00BB "+name; } public boolean isBuildBlocked() { return owner.isBuildBlocked(); } @SuppressWarnings("deprecation") public String getWhyBlocked() { return owner.getWhyBlocked(); } public CauseOfBlockage getCauseOfBlockage() { return owner.getCauseOfBlockage(); } public String getName() { return name; } public boolean isConcurrentBuild() { return false; } public long getEstimatedDuration() { BatchRun b = getLastSuccessfulRun(); if(b==null) return -1; long duration = b.getDuration(); if(duration==0) return -1; return duration; } public Label getAssignedLabel() { Node on = owner.getLastBuiltOn(); if(on==null) return null; return on.getSelfLabel(); } public Node getLastBuiltOn() { return owner.getLastBuiltOn(); } public String getBuildStatusUrl() { return getIconColor()+".gif"; } public BallColor getIconColor() { BatchRun r = getLastRun(); if(r==null) return BallColor.GREY; else return r.getIconColor(); } /** * Obtains the latest {@link BatchRun} record. */ public BatchRun getLastRun() { for(AbstractBuild<?,?> b : owner.getBuilds()) { BatchRunAction bra = b.getAction(BatchRunAction.class); if(bra==null) continue; for (BatchRun br : bra.records) { if(br.taskName.equals(name)) return br; } } return null; } public BatchRun getLastSuccessfulRun() { for(BatchRun r=getLastRun(); r!=null; r=r.getPrevious()) if(r.getResult()== Result.SUCCESS) return r; return null; } public BatchRun getLastFailedRun() { for(BatchRun r=getLastRun(); r!=null; r=r.getPrevious()) if(r.getResult()==Result.FAILURE) return r; return null; } /** * Gets all the run records. */ public Iterable<BatchRun> getRuns() { return new Iterable<BatchRun>() { public Iterator<BatchRun> iterator() { return new Iterators.FlattenIterator<BatchRun,AbstractBuild<?,?>>(owner.getBuilds().iterator()) { protected Iterator<BatchRun> expand(AbstractBuild<?,?> b) { BatchRunAction a = b.getAction(BatchRunAction.class); if(a==null) return Iterators.empty(); else return a.getRecords(name).iterator(); } }; } }; } public HistoryWidget createHistoryWidget() { return new BuildHistoryWidget<BatchRun>(this,getRuns(),ADAPTER); } public BatchRun createExecutable() throws IOException { AbstractBuild<?,?> lb = owner.getLastBuild(); if (lb == null) return null; BatchRunAction records; synchronized (lb) { records = lb.getAction(BatchRunAction.class); if(records==null) { records = new BatchRunAction(lb); lb.addAction(records); // we don't need to save it yet. } } return records.createRecord(this); } /** * Gets the expected build number assigned to the next run. * * @return string like "5-3" */ public String getNextBuildNumber() { AbstractBuild<?,?> lb = owner.getLastBuild(); if(lb==null) return "0-0"; int id=1; BatchRunAction records = lb.getAction(BatchRunAction.class); if(records!=null) id=records.getRecords().size()+1; return lb.getNumber()+"-"+id; } /** * Returns the {@link ACL} for this object. */ public ACL getACL() { return owner.getACL(); } public void checkAbortPermission() { getACL().checkPermission(AbstractProject.ABORT); } public boolean hasAbortPermission() { return getACL().hasPermission(AbstractProject.ABORT); } public boolean hasBuildPermission() { return getACL().hasPermission(AbstractProject.BUILD); } public boolean hasDeletePermission() { return getACL().hasPermission(AbstractProject.DELETE); } public boolean hasConfigurePermission() { return getACL().hasPermission(AbstractProject.CONFIGURE); } /** * {@link BatchTask} requires exclusive access to the workspace. */ public ResourceList getResourceList() { return new ResourceList().w(owner.getWorkspaceResource()); } public Object getDynamic(String token, StaplerRequest req, StaplerResponse rsp) { Matcher m = BUILD_NUMBER_PATTERN.matcher(token); if(m.matches()) { AbstractBuild<?,?> b = owner.getBuildByNumber(Integer.parseInt(m.group(1))); if(b==null) return null; BatchRunAction a = b.getAction(BatchRunAction.class); if(a==null) return null; return a.getRecord(Integer.parseInt(m.group(2))); } return null; } /** * Schedules the execution */ public synchronized void doExecute( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { getACL().checkPermission(AbstractProject.BUILD); if (owner.getLastBuild() != null) { Hudson.getInstance().getQueue().schedule(this,0,new CauseAction(new UserCause())); rsp.forwardToPreviousPage(req); } else { rsp.sendRedirect2("noBuild"); } } /** * Deletes this task. */ public synchronized void doDoDelete(StaplerResponse rsp) throws IOException, ServletException { getACL().checkPermission(AbstractProject.DELETE); getParent().removeTask(this); rsp.sendRedirect2("../.."); } private static final Adapter<BatchRun> ADAPTER = new Adapter<BatchRun>() { public int compare(BatchRun record, String key) { int[] lhs = parse(record.getNumber()); int[] rhs = parse(key); int d = lhs[0]-rhs[0]; if(d!=0) return d; return lhs[1]-rhs[1]; } public String getKey(BatchRun record) { return record.getNumber(); } public boolean isBuilding(BatchRun record) { return record.isRunning(); } public String getNextKey(String key) { int[] r = parse(key); r[1]++; return r[0]+"-"+r[1]; } private int[] parse(String num) { Matcher m = BUILD_NUMBER_PATTERN.matcher(num); if(m.matches()) return new int[]{Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)) }; return new int[]{0,0}; } }; private static final Pattern BUILD_NUMBER_PATTERN = Pattern.compile("(\\d+)-(\\d+)"); public void doCancelQueue(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { checkAbortPermission(); Hudson.getInstance().getQueue().cancel(this); rsp.forwardToPreviousPage(req); } public String getUrl() { return owner.getUrl() + "batchTasks/task/" + name + "/"; } static { // Used when BatchTask is in Queue at Hudson shutdown Queue.XSTREAM.registerConverter(new AbstractSingleValueConverter() { @Override public boolean canConvert(Class klazz) { return BatchTask.class==klazz; } @Override public Object fromString(String str) { int idx=str.lastIndexOf('/'); if(idx<0) throw new NoSuchElementException("Illegal format: "+str); String projectName = str.substring(0, idx); Job<?,?> job = (Job<?,?>) Hudson.getInstance().getItemByFullName(projectName); if(job==null) throw new NoSuchElementException("No such job exists: "+projectName); BatchTaskProperty bp = job.getProperty(BatchTaskProperty.class); if(bp==null) throw new NoSuchElementException(projectName+" doesn't have the batck task anymore"); return bp.getTask(str.substring(idx+1)); } @Override public String toString(Object item) { BatchTask bt = (BatchTask) item; return bt.owner.getFullName()+"/"+bt.name; } }); } }