package hudson.plugins.downstream_ext;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.Cause;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.DependencyGraph.Dependency;
import hudson.util.LogTaskListener;
import hudson.util.StreamTaskListener;
import java.io.PrintStream;
import java.io.Serializable;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Defines a dependency introduced by the downstream-ext plugin.
*
* @author kutzi
*/
public class DownstreamDependency extends Dependency {
private static final Logger LOGGER = Logger.getLogger(DownstreamDependency.class.getName());
private final DownstreamTrigger trigger;
public DownstreamDependency(AbstractProject<?, ?> upstream, AbstractProject<?, ?> downstream,
DownstreamTrigger trigger) {
super(upstream, downstream);
this.trigger = trigger;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public boolean shouldTriggerBuild(AbstractBuild build,
TaskListener listener, List<Action> actions) {
PrintStream logger = listener.getLogger();
if(trigger.getStrategy().evaluate(trigger.getThreshold(), build.getResult())) {
AbstractProject p = getDownstreamProject();
if(trigger.isOnlyIfSCMChanges()) {
if (p.getScm().requiresWorkspaceForPolling()) {
// Downstream project locks workspace while building.
// If polled synchronously this could make the upstream build
// lock for a possibly long time.
// See HUDSON-5406
logger.println(Messages.DownstreamTrigger_StartedAsynchPoll(p.getName()));
Runnable run = getPoller(p, new Cause.UpstreamCause((Run<?,?>)build), actions);
DownstreamTrigger.executeForProject(p, run);
return false;
}
if (p.pollSCMChanges(listener)) {
return true;
} else {
logger.println(Messages.DownstreamTrigger_NoSCMChanges(p.getName()));
return false;
}
}
return true;
} else {
logger.println(Messages.DownstreamTrigger_ConditionNotMet(trigger.getStrategy().getDisplayName(),
trigger.getThreshold()));
return false;
}
}
// Technically it'd be safe to not override equals
// since superclass implements it well.
// But maybe that changes in the future.
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DownstreamDependency)) {
return false;
}
// Currently, there can be only one downstream-ext dependency per project
// If that'd change later we must check the trigger instance here, too.
return super.equals(obj);
}
@SuppressWarnings("unchecked")
Runnable getPoller(AbstractProject p, Cause cause, List<Action> actions) {
return new PollRunner(p, cause, actions);
}
@SuppressWarnings("unchecked")
private static class PollRunner implements Runnable {
private final AbstractProject project;
private final Cause cause;
private final List<Action> buildActions;
private final TaskListener taskListener;
public PollRunner(AbstractProject p, Cause cause, List<Action> actions) {
this.project = p;
this.cause = cause;
this.buildActions = actions;
// workaround for HUDSON-5406:
// some (all?) SCMs require a serializable TaskListener for AbstractProject#pollSCMChanges
// LogTaskListener is not serializable (at least not up until Hudson 1.352)
TaskListener tl = new LogTaskListener(LOGGER, Level.INFO);
if (tl instanceof Serializable) {
this.taskListener = tl;
} else {
this.taskListener = new StreamTaskListener(System.out);
}
}
@Override
public void run() {
LOGGER.info("Polling for SCM changes in " + this.project.getName());
if(this.project.pollSCMChanges(this.taskListener)) {
LOGGER.info("SCM changes found for " + this.project.getName() + ". Triggering build.");
if (this.project.scheduleBuild(this.project.getQuietPeriod(), this.cause,
buildActions.toArray(new Action[buildActions.size()]))) {
LOGGER.info("Build of " + this.project.getName() + " scheduled successfully.");
} else {
LOGGER.info("No build of " + this.project.getName() + " scheduled - this usually means that another build is already in the queue.");
}
} else {
LOGGER.info(Messages.DownstreamTrigger_NoSCMChanges(this.project.getName()));
}
}
}
}