diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index 93332b051116cd296dbfdd9f3d73c42c0ee18d56..58b9cbe13e1e91c778f97f61825a5d11c21ef232 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -45,7 +45,6 @@ import hudson.model.Descriptor.FormException; import hudson.model.Fingerprint.RangeSet; import hudson.model.Queue.Executable; import hudson.model.Queue.Task; -import hudson.model.listeners.SCMListener; import hudson.model.queue.SubTask; import hudson.model.Queue.WaitingItem; import hudson.model.RunMap.Constructor; @@ -56,7 +55,6 @@ import hudson.model.queue.CauseOfBlockage; import hudson.model.queue.SubTaskContributor; import hudson.scm.ChangeLogSet; import hudson.scm.ChangeLogSet.Entry; -import hudson.scm.NullChangeLogParser; import hudson.scm.NullSCM; import hudson.scm.PollingResult; import hudson.scm.SCM; @@ -79,7 +77,6 @@ import hudson.util.AlternativeUiTextProvider.Message; import hudson.util.DescribableList; import hudson.util.EditDistance; import hudson.util.FormValidation; -import hudson.util.IOException2; import hudson.widgets.BuildHistoryWidget; import hudson.widgets.HistoryWidget; import jenkins.model.Jenkins; @@ -91,7 +88,6 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; -import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.ForwardToView; import org.kohsuke.stapler.HttpRedirect; import org.kohsuke.stapler.HttpResponse; @@ -105,8 +101,6 @@ import org.kohsuke.stapler.interceptor.RequirePOST; import javax.servlet.ServletException; import java.io.File; import java.io.IOException; -import java.io.InterruptedIOException; -import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; @@ -1318,86 +1312,93 @@ public abstract class AbstractProject
,R extends A } try { - try { - SCMPollListener.fireBeforePolling(this, listener); - } catch( Exception e ) { - /* Make sure, that the listeners do not have any impact on the actual poll */ - } - if (scm.requiresWorkspaceForPolling()) { - // lock the workspace of the last build - FilePath ws=lb.getWorkspace(); - - if (workspaceOffline(lb)) { - // workspace offline. build now, or nothing will ever be built - Label label = getAssignedLabel(); - if (label != null && label.isSelfLabel()) { - // if the build is fixed on a node, then attempting a build will do us - // no good. We should just wait for the slave to come back. - listener.getLogger().println(Messages.AbstractProject_NoWorkspace()); - return NO_CHANGES; - } - listener.getLogger().println( ws==null - ? Messages.AbstractProject_WorkspaceOffline() - : Messages.AbstractProject_NoWorkspace()); - if (isInQueue()) { - listener.getLogger().println(Messages.AbstractProject_AwaitingBuildForWorkspace()); - return NO_CHANGES; - } else { - listener.getLogger().println(Messages.AbstractProject_NewBuildForWorkspace()); - return BUILD_NOW; - } - } else { - WorkspaceList l = lb.getBuiltOn().toComputer().getWorkspaceList(); - // if doing non-concurrent build, acquire a workspace in a way that causes builds to block for this workspace. - // this prevents multiple workspaces of the same job --- the behavior of Hudson < 1.319. - // - // OTOH, if a concurrent build is chosen, the user is willing to create a multiple workspace, - // so better throughput is achieved over time (modulo the initial cost of creating that many workspaces) - // by having multiple workspaces - WorkspaceList.Lease lease = l.acquire(ws, !concurrentBuild); - Launcher launcher = ws.createLauncher(listener); - try { - LOGGER.fine("Polling SCM changes of " + getName()); - if (pollingBaseline==null) // see NOTE-NO-BASELINE above - calcPollingBaseline(lb,launcher,listener); - PollingResult r = scm.poll(this, launcher, ws, listener, pollingBaseline); - pollingBaseline = r.remote; - return r; - } finally { - lease.release(); - } - } - } else { - // polling without workspace - LOGGER.fine("Polling SCM changes of " + getName()); - - if (pollingBaseline==null) // see NOTE-NO-BASELINE above - calcPollingBaseline(lb,null,listener); - PollingResult r = scm.poll(this, null, null, listener, pollingBaseline); - pollingBaseline = r.remote; - return r; - } + SCMPollListener.fireBeforePolling(this, listener); + PollingResult r = _poll(listener, scm, lb); + SCMPollListener.firePollingSuccess(this,listener, r); + return r; } catch (AbortException e) { listener.getLogger().println(e.getMessage()); listener.fatalError(Messages.AbstractProject_Aborted()); LOGGER.log(Level.FINE, "Polling "+this+" aborted",e); + SCMPollListener.firePollingFailed(this, listener,e); return NO_CHANGES; } catch (IOException e) { e.printStackTrace(listener.fatalError(e.getMessage())); + SCMPollListener.firePollingFailed(this, listener,e); return NO_CHANGES; } catch (InterruptedException e) { e.printStackTrace(listener.fatalError(Messages.AbstractProject_PollingABorted())); + SCMPollListener.firePollingFailed(this, listener,e); return NO_CHANGES; - } finally { - /* Do this no matter what */ - try { - SCMPollListener.fireAfterPolling(this, listener); - } catch( Exception e ) { - /* Make sure, that the listeners do not have any impact on the actual poll */ - } + } catch (RuntimeException e) { + SCMPollListener.firePollingFailed(this, listener,e); + throw e; + } catch (Error e) { + SCMPollListener.firePollingFailed(this, listener,e); + throw e; } } - + + /** + * {@link #poll(TaskListener)} method without the try/catch block that does listener notification and . + */ + private PollingResult _poll(TaskListener listener, SCM scm, R lb) throws IOException, InterruptedException { + if (scm.requiresWorkspaceForPolling()) { + // lock the workspace of the last build + FilePath ws=lb.getWorkspace(); + + if (workspaceOffline(lb)) { + // workspace offline. build now, or nothing will ever be built + Label label = getAssignedLabel(); + if (label != null && label.isSelfLabel()) { + // if the build is fixed on a node, then attempting a build will do us + // no good. We should just wait for the slave to come back. + listener.getLogger().println(Messages.AbstractProject_NoWorkspace()); + return NO_CHANGES; + } + listener.getLogger().println( ws==null + ? Messages.AbstractProject_WorkspaceOffline() + : Messages.AbstractProject_NoWorkspace()); + if (isInQueue()) { + listener.getLogger().println(Messages.AbstractProject_AwaitingBuildForWorkspace()); + return NO_CHANGES; + } else { + listener.getLogger().println(Messages.AbstractProject_NewBuildForWorkspace()); + return BUILD_NOW; + } + } else { + WorkspaceList l = lb.getBuiltOn().toComputer().getWorkspaceList(); + // if doing non-concurrent build, acquire a workspace in a way that causes builds to block for this workspace. + // this prevents multiple workspaces of the same job --- the behavior of Hudson < 1.319. + // + // OTOH, if a concurrent build is chosen, the user is willing to create a multiple workspace, + // so better throughput is achieved over time (modulo the initial cost of creating that many workspaces) + // by having multiple workspaces + WorkspaceList.Lease lease = l.acquire(ws, !concurrentBuild); + Launcher launcher = ws.createLauncher(listener); + try { + LOGGER.fine("Polling SCM changes of " + getName()); + if (pollingBaseline==null) // see NOTE-NO-BASELINE above + calcPollingBaseline(lb,launcher,listener); + PollingResult r = scm.poll(this, launcher, ws, listener, pollingBaseline); + pollingBaseline = r.remote; + return r; + } finally { + lease.release(); + } + } + } else { + // polling without workspace + LOGGER.fine("Polling SCM changes of " + getName()); + + if (pollingBaseline==null) // see NOTE-NO-BASELINE above + calcPollingBaseline(lb,null,listener); + PollingResult r = scm.poll(this, null, null, listener, pollingBaseline); + pollingBaseline = r.remote; + return r; + } + } + private boolean workspaceOffline(R build) throws IOException, InterruptedException { FilePath ws = build.getWorkspace(); if (ws==null || !ws.exists()) { diff --git a/core/src/main/java/hudson/model/listeners/SCMPollListener.java b/core/src/main/java/hudson/model/listeners/SCMPollListener.java index 3ad8d14ff922f456fdfa1e9200ea697899574a29..1a5a2bf786d7bef4cee1a24f0d96d6a0528e77d9 100644 --- a/core/src/main/java/hudson/model/listeners/SCMPollListener.java +++ b/core/src/main/java/hudson/model/listeners/SCMPollListener.java @@ -25,28 +25,78 @@ package hudson.model.listeners; import hudson.ExtensionList; import hudson.ExtensionPoint; +import hudson.scm.PollingResult; import jenkins.model.Jenkins; import hudson.model.AbstractProject; import hudson.model.TaskListener; -public abstract class SCMPollListener implements ExtensionPoint { +import java.io.IOException; +/** + * A hook for listening to polling activities in Jenkins. + * + * @author Christian Wolfgang + * @author Kohsuke Kawaguchi + * @since 1.474 + */ +public abstract class SCMPollListener implements ExtensionPoint { + /** + * Called before the polling execution. + * + * @param project + * Project that's about to run polling. + * @param listener + * Connected to the polling log. + */ public void onBeforePolling( AbstractProject, ?> project, TaskListener listener ) {} - public void onAfterPolling( AbstractProject, ?> project, TaskListener listener ) {} + /** + * Called when the polling successfully concluded. + * + * @param result + * The result of the polling. + */ + public void onPollingSuccess( AbstractProject, ?> project, TaskListener listener, PollingResult result) {} + + /** + * Called when the polling concluded with an error. + * + * @param exception + * The problem reported. This can include {@link InterruptedException} (that corresponds to the user cancelling it), + * some anticipated problems like {@link IOException}, or bug in the code ({@link RuntimeException}) + */ + public void onPollingFailed( AbstractProject, ?> project, TaskListener listener, Throwable exception) {} public static void fireBeforePolling( AbstractProject, ?> project, TaskListener listener ) { - for( SCMPollListener l : all() ) { - l.onBeforePolling( project, listener ); - } - } + for (SCMPollListener l : all()) { + try { + l.onBeforePolling(project, listener); + } catch (Exception e) { + /* Make sure, that the listeners do not have any impact on the actual poll */ + } + } + } - public static void fireAfterPolling( AbstractProject, ?> project, TaskListener listener ) { + public static void firePollingSuccess( AbstractProject, ?> project, TaskListener listener, PollingResult result ) { for( SCMPollListener l : all() ) { - l.onAfterPolling( project, listener ); + try { + l.onPollingSuccess(project, listener, result); + } catch (Exception e) { + /* Make sure, that the listeners do not have any impact on the actual poll */ + } } } + public static void firePollingFailed( AbstractProject, ?> project, TaskListener listener, Throwable exception ) { + for( SCMPollListener l : all() ) { + try { + l.onPollingFailed(project, listener, exception); + } catch (Exception e) { + /* Make sure, that the listeners do not have any impact on the actual poll */ + } + } + } + /** * Returns all the registered {@link SCMPollListener}s. */