package join; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Action; import hudson.model.BuildListener; import hudson.model.Descriptor; import hudson.model.Hudson; import hudson.model.Result; import hudson.model.Run; import hudson.model.TaskListener; import hudson.tasks.BuildTrigger; import hudson.tasks.Publisher; import hudson.util.DescribableList; import hudson.model.Items; import hudson.model.Cause.UpstreamCause; public class JoinAction implements Action { private List<String> pendingDownstreamProjects; private List<String> completedDownstreamProjects; private transient String joinProjects; private DescribableList<Publisher, Descriptor<Publisher>> joinPublishers; private boolean evenIfDownstreamUnstable; private Result overallResult; public JoinAction(JoinTrigger joinTrigger, BuildTrigger buildTrigger, ArrayList<String> otherDownstream) { this.pendingDownstreamProjects = new LinkedList<String>(); if(buildTrigger != null) { for(AbstractProject project : buildTrigger.getChildProjects()) { if(!project.isDisabled()) { this.pendingDownstreamProjects.add(project.getName()); } } } for(String proj : otherDownstream) { this.pendingDownstreamProjects.add(proj.trim()); } this.joinProjects = joinTrigger.getJoinProjectsValue(); this.joinPublishers = joinTrigger.getJoinPublishers(); this.evenIfDownstreamUnstable = joinTrigger.getEvenIfDownstreamUnstable(); this.completedDownstreamProjects = new LinkedList<String>(); this.overallResult = Result.SUCCESS; } public String getDisplayName() { return null; } public String getIconFileName() { return null; } public String getUrlName() { return "join"; } // upstreamBuild is the build that contains this JoinAction. public void downstreamFinished(AbstractBuild<?,?> upstreamBuild, AbstractBuild<?,?> finishedBuild, TaskListener listener) { String finishedBuildProjectName = finishedBuild.getProject().getName(); if(pendingDownstreamProjects.remove(finishedBuildProjectName)) { this.overallResult = this.overallResult.combine(finishedBuild.getResult()); completedDownstreamProjects.add(finishedBuildProjectName); checkPendingDownstream(upstreamBuild, listener); } else { listener.getLogger().println("[Join] Pending does not contain " + finishedBuildProjectName); } } public void checkPendingDownstream(AbstractBuild<?,?> owner, TaskListener listener) { if(pendingDownstreamProjects.isEmpty()) { listener.getLogger().println("All downstream projects complete!"); Result threshold = this.evenIfDownstreamUnstable ? Result.UNSTABLE : Result.SUCCESS; if(this.overallResult.isWorseThan(threshold)) { listener.getLogger().println("Minimum result threshold not met for join project"); } else { for(Publisher pub : this.joinPublishers) { try { pub.perform(owner, null, (BuildListener)listener); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } List<AbstractProject> projects = Items.fromNameList(joinProjects, AbstractProject.class); for(AbstractProject project : projects) { listener.getLogger().println("Scheduling join project: " + project.getName()); project.scheduleBuild(new JoinCause(owner)); } } } else { listener.getLogger().println("Project " + owner.getProject().getName() + " still waiting for " + pendingDownstreamProjects.size() + " builds to complete"); } } public class JoinCause extends UpstreamCause { public JoinCause(Run<?, ?> arg0) { super(arg0); } } }