Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FIXED JENKINS-14178] Merge branch 'pull-505'
Conflicts:
	core/src/main/java/hudson/model/AbstractProject.java
  • Loading branch information
kohsuke committed Jun 29, 2012
2 parents d1d66fe + d46ba52 commit f5d378f
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 60 deletions.
3 changes: 3 additions & 0 deletions changelog.html
Expand Up @@ -58,6 +58,9 @@
<li class=bug>
Fix French translation
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-13274">issue 13274</a>)
<li class=rfe>
Added a new extension point to listen to polling activities.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-14178">issue 14178</a>)
</ul>
</div><!--=TRUNK-END=-->

Expand Down
135 changes: 75 additions & 60 deletions core/src/main/java/hudson/model/AbstractProject.java
Expand Up @@ -53,11 +53,11 @@
import hudson.model.RunMap.Constructor;
import hudson.model.labels.LabelAtom;
import hudson.model.labels.LabelExpression;
import hudson.model.listeners.SCMPollListener;
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;
Expand All @@ -80,7 +80,6 @@
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;
Expand All @@ -92,7 +91,6 @@
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;
Expand All @@ -106,8 +104,6 @@
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;
Expand Down Expand Up @@ -1324,74 +1320,93 @@ public PollingResult poll( TaskListener listener ) {
}

try {
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;
} 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()) {
Expand Down
106 changes: 106 additions & 0 deletions core/src/main/java/hudson/model/listeners/SCMPollListener.java
@@ -0,0 +1,106 @@
/*
* The MIT License
*
* Copyright (c) 2011, Christian Wolfgang
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
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;

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 ) {}

/**
* 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()) {
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 firePollingSuccess( AbstractProject<?, ?> project, TaskListener listener, PollingResult result ) {
for( SCMPollListener l : all() ) {
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.
*/
public static ExtensionList<SCMPollListener> all() {
return Jenkins.getInstance().getExtensionList( SCMPollListener.class );
}
}

0 comments on commit f5d378f

Please sign in to comment.