diff --git a/changelog.html b/changelog.html
index 58ade6d74f..edd8974079 100644
--- a/changelog.html
+++ b/changelog.html
@@ -55,7 +55,9 @@ Upcoming changes
- -
+
-
+ Node monitoring now happens concurrently across all the slaves, so it'll be affected less by problematic slaves.
+ (issue 18438)
diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java
index cf926539c1..6bf1f387ca 100644
--- a/core/src/main/java/hudson/FilePath.java
+++ b/core/src/main/java/hudson/FilePath.java
@@ -70,6 +70,7 @@ import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InterruptedIOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
@@ -998,6 +999,27 @@ public final class FilePath implements Serializable {
}
}
+ /**
+ * Takes a {@link FilePath}+{@link FileCallable} pair and returns the equivalent {@link Callable}.
+ * When executing the resulting {@link Callable}, it executes {@link FileCallable#act(FileCallable)}
+ * on this {@link FilePath}.
+ *
+ * @since 1.522
+ */
+ public Callable asCallableWith(final FileCallable task) {
+ return new Callable() {
+ @Override
+ public V call() throws IOException {
+ try {
+ return act(task);
+ } catch (InterruptedException e) {
+ throw (IOException)new InterruptedIOException().initCause(e);
+ }
+ }
+ private static final long serialVersionUID = 1L;
+ };
+ }
+
/**
* Converts this file to the URI, relative to the machine
* on which this file is available.
diff --git a/core/src/main/java/hudson/model/Node.java b/core/src/main/java/hudson/model/Node.java
index 59fe9f7530..9da535694a 100644
--- a/core/src/main/java/hudson/model/Node.java
+++ b/core/src/main/java/hudson/model/Node.java
@@ -35,6 +35,7 @@ import hudson.model.Queue.Task;
import hudson.model.labels.LabelAtom;
import hudson.model.queue.CauseOfBlockage;
import hudson.node_monitors.NodeMonitor;
+import hudson.remoting.Callable;
import hudson.remoting.VirtualChannel;
import hudson.security.ACL;
import hudson.security.AccessControlled;
@@ -445,7 +446,22 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable
* @throws InterruptedException
* if the operation is aborted.
*/
- public abstract ClockDifference getClockDifference() throws IOException, InterruptedException;
+ public ClockDifference getClockDifference() throws IOException, InterruptedException {
+ VirtualChannel channel = getChannel();
+ if(channel==null)
+ throw new IOException(getNodeName()+" is offline");
+
+ return channel.call(getClockDifferenceCallable());
+ }
+
+ /**
+ * Returns a {@link Callable} that when run on the channel, estimates the clock difference.
+ *
+ * @return
+ * always non-null.
+ * @sine 1.522
+ */
+ public abstract Callable getClockDifferenceCallable();
/**
* Constants that control how Hudson allocates jobs to slaves.
diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java
index 7318f8bebe..b907542185 100644
--- a/core/src/main/java/hudson/model/Slave.java
+++ b/core/src/main/java/hudson/model/Slave.java
@@ -30,7 +30,6 @@ import hudson.Util;
import hudson.Launcher.RemoteLauncher;
import hudson.model.Descriptor.FormException;
import hudson.remoting.Callable;
-import hudson.remoting.VirtualChannel;
import hudson.slaves.CommandLauncher;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.DumbSlave;
@@ -57,6 +56,7 @@ import java.util.Set;
import javax.servlet.ServletException;
+import hudson.util.TimeUnit2;
import jenkins.model.Jenkins;
import jenkins.slaves.WorkspaceLocator;
@@ -252,16 +252,9 @@ public abstract class Slave extends Node implements Serializable {
getAssignedLabels();
}
- public ClockDifference getClockDifference() throws IOException, InterruptedException {
- VirtualChannel channel = getChannel();
- if(channel==null)
- throw new IOException(getNodeName()+" is offline");
-
- long startTime = System.currentTimeMillis();
- long slaveTime = channel.call(new GetSystemTime());
- long endTime = System.currentTimeMillis();
-
- return new ClockDifference((startTime+endTime)/2 - slaveTime);
+ @Override
+ public Callable getClockDifferenceCallable() {
+ return new GetClockDifference1();
}
public Computer createComputer() {
@@ -432,16 +425,59 @@ public abstract class Slave extends Node implements Serializable {
private transient String agentCommand;
/**
- * Obtains the system clock.
+ * Obtains the clock difference between this side and that side of the channel.
+ *
+ *
+ * This is a hack to wrap the whole thing into a simple {@link Callable}.
+ *
+ *
+ * - When the callable is sent to remote, we capture the time (on this side) in {@link GetClockDifference2#startTime}
+ *
- When the other side receives the callable it is {@link GetClockDifference2}.
+ *
- We capture the time on the other side and {@link GetClockDifference3} gets sent from the other side
+ *
- When it's read on this side as a return value, it morphs itself into {@link ClockDifference}.
+ *
*/
- private static final class GetSystemTime implements Callable {
- public Long call() {
- return System.currentTimeMillis();
+ private static final class GetClockDifference1 implements Callable {
+ public ClockDifference call() {
+ // this method must be being invoked locally, which means the clock is in sync
+ return new ClockDifference(0);
+ }
+
+ private Object writeReplace() {
+ return new GetClockDifference2();
}
private static final long serialVersionUID = 1L;
}
+ private static final class GetClockDifference2 implements Callable {
+ /**
+ * Capture the time on the master when this object is sent to remote, which is when
+ * {@link GetClockDifference1#writeReplace()} is run.
+ */
+ private final long startTime = System.nanoTime();
+
+ public GetClockDifference3 call() {
+ return new GetClockDifference3(startTime);
+ }
+
+ private static final long serialVersionUID = 1L;
+ }
+
+ private static final class GetClockDifference3 implements Serializable {
+ private final long remoteTime = System.nanoTime();
+ private final long startTime;
+
+ public GetClockDifference3(long startTime) {
+ this.startTime = startTime;
+ }
+
+ private Object readResolve() {
+ long endTime = System.nanoTime();
+ return new ClockDifference(TimeUnit2.NANOSECONDS.toMillis((startTime + endTime)/2-remoteTime));
+ }
+ }
+
/**
* Determines the workspace root file name for those who really really need the shortest possible path name.
*/
diff --git a/core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java b/core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java
new file mode 100644
index 0000000000..bfb88d264e
--- /dev/null
+++ b/core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java
@@ -0,0 +1,119 @@
+package hudson.node_monitors;
+
+import hudson.model.Computer;
+import hudson.remoting.Callable;
+import hudson.remoting.VirtualChannel;
+import jenkins.model.Jenkins;
+
+import javax.annotation.CheckForNull;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.logging.Level.WARNING;
+
+/**
+ * Sophisticated version of {@link AbstractNodeMonitorDescriptor} that
+ * performs monitoring on all slaves concurrently and asynchronously.
+ *
+ * @param
+ * represents the the result of the monitoring.
+ * @author Kohsuke Kawaguchi
+ */
+public abstract class AbstractAsyncNodeMonitorDescriptor extends AbstractNodeMonitorDescriptor {
+ protected AbstractAsyncNodeMonitorDescriptor() {
+ }
+
+ protected AbstractAsyncNodeMonitorDescriptor(long interval) {
+ super(interval);
+ }
+
+ protected AbstractAsyncNodeMonitorDescriptor(Class extends NodeMonitor> clazz) {
+ super(clazz);
+ }
+
+ protected AbstractAsyncNodeMonitorDescriptor(Class extends NodeMonitor> clazz, long interval) {
+ super(clazz, interval);
+ }
+
+ /**
+ * Creates a {@link Callable} that performs the monitoring when executed.
+ */
+ protected abstract @CheckForNull Callable createCallable(Computer c);
+
+ @Override
+ protected T monitor(Computer c) throws IOException, InterruptedException {
+ VirtualChannel ch = c.getChannel();
+ if (ch != null) {
+ Callable cc = createCallable(c);
+ if (cc!=null)
+ return ch.call(cc);
+ }
+ return null;
+ }
+
+ /**
+ * Performs all monitoring concurrently.
+ */
+ @Override
+ protected Map monitor() throws InterruptedException {
+ Map> futures = new HashMap>();
+
+ for (Computer c : Jenkins.getInstance().getComputers()) {
+ try {
+ VirtualChannel ch = c.getChannel();
+ futures.put(c,null); // sentinel value
+ if (ch!=null) {
+ Callable cc = createCallable(c);
+ if (cc!=null)
+ futures.put(c,ch.callAsync(cc));
+ }
+ } catch (RuntimeException e) {
+ LOGGER.log(WARNING, "Failed to monitor "+c.getDisplayName()+" for "+getDisplayName(), e);
+ } catch (IOException e) {
+ LOGGER.log(WARNING, "Failed to monitor "+c.getDisplayName()+" for "+getDisplayName(), e);
+ }
+ }
+
+ final long now = System.currentTimeMillis();
+ final long end = now + getMonitoringTimeOut();
+
+ final Map data = new HashMap();
+
+ for (Entry> e : futures.entrySet()) {
+ Computer c = e.getKey();
+ Future f = futures.get(c);
+ data.put(c, null); // sentinel value
+
+ if (f!=null) {
+ try {
+ data.put(c,f.get(Math.max(0,end-System.currentTimeMillis()), MILLISECONDS));
+ } catch (RuntimeException x) {
+ LOGGER.log(WARNING, "Failed to monitor " + c.getDisplayName() + " for " + getDisplayName(), x);
+ } catch (ExecutionException x) {
+ LOGGER.log(WARNING, "Failed to monitor " + c.getDisplayName() + " for " + getDisplayName(), x);
+ } catch (TimeoutException x) {
+ LOGGER.log(WARNING, "Failed to monitor " + c.getDisplayName() + " for " + getDisplayName(), x);
+ }
+ }
+ }
+
+ return data;
+ }
+
+ /**
+ * Controls the time out of monitoring.
+ */
+ protected long getMonitoringTimeOut() {
+ return TimeUnit.SECONDS.toMillis(30);
+ }
+
+ private static final Logger LOGGER = Logger.getLogger(AbstractAsyncNodeMonitorDescriptor.class.getName());
+}
diff --git a/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java b/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java
index 160951e65c..3e57ea5bfe 100644
--- a/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java
+++ b/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java
@@ -34,6 +34,7 @@ import hudson.triggers.SafeTimerTask;
import hudson.slaves.OfflineCause;
import java.io.IOException;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -51,18 +52,34 @@ import java.util.logging.Logger;
* @author Kohsuke Kawaguchi
*/
public abstract class AbstractNodeMonitorDescriptor extends Descriptor {
+ /**
+ * @deprecated as of 1.522
+ * Extend from {@link AbstractAsyncNodeMonitorDescriptor}
+ */
protected AbstractNodeMonitorDescriptor() {
this(HOUR);
}
+ /**
+ * @deprecated as of 1.522
+ * Extend from {@link AbstractAsyncNodeMonitorDescriptor}
+ */
protected AbstractNodeMonitorDescriptor(long interval) {
schedule(interval);
}
+ /**
+ * @deprecated as of 1.522
+ * Extend from {@link AbstractAsyncNodeMonitorDescriptor}
+ */
protected AbstractNodeMonitorDescriptor(Class extends NodeMonitor> clazz) {
this(clazz,HOUR);
}
+ /**
+ * @deprecated as of 1.522
+ * Extend from {@link AbstractAsyncNodeMonitorDescriptor}
+ */
protected AbstractNodeMonitorDescriptor(Class extends NodeMonitor> clazz, long interval) {
super(clazz);
@@ -106,6 +123,33 @@ public abstract class AbstractNodeMonitorDescriptor extends Descriptor monitor() throws InterruptedException {
+ Map data = new HashMap();
+ for( Computer c : Jenkins.getInstance().getComputers() ) {
+ try {
+ Thread.currentThread().setName("Monitoring "+c.getDisplayName()+" for "+getDisplayName());
+
+ if(c.getChannel()==null)
+ data.put(c,null);
+ else
+ data.put(c,monitor(c));
+ } catch (RuntimeException e) {
+ LOGGER.log(Level.WARNING, "Failed to monitor "+c.getDisplayName()+" for "+getDisplayName(), e);
+ } catch (IOException e) {
+ LOGGER.log(Level.WARNING, "Failed to monitor "+c.getDisplayName()+" for "+getDisplayName(), e);
+ } catch (InterruptedException e) {
+ throw (InterruptedException)new InterruptedException("Node monitoring "+c.getDisplayName()+" for "+getDisplayName()+" aborted.").initCause(e);
+ }
+ }
+ return data;
+ }
+
/**
* Obtains the monitoring result currently available, or null if no data is available.
*
@@ -214,7 +258,7 @@ public abstract class AbstractNodeMonitorDescriptor extends Descriptor data = new HashMap();
+ private /*final*/ Map data = Collections.emptyMap();
private long timestamp;
@@ -235,24 +279,7 @@ public abstract class AbstractNodeMonitorDescriptor extends Descriptor {
- protected String monitor(Computer c) throws IOException, InterruptedException {
- return c.getChannel().call(new GetArchTask());
+ public static final class DescriptorImpl extends AbstractAsyncNodeMonitorDescriptor {
+ @Override
+ protected Callable createCallable(Computer c) {
+ return new GetArchTask();
}
public String getDisplayName() {
@@ -55,7 +56,7 @@ public class ArchitectureMonitor extends NodeMonitor {
/**
* Obtains the string that represents the architecture.
*/
- private static class GetArchTask implements Callable {
+ private static class GetArchTask implements Callable {
public String call() {
String os = System.getProperty("os.name");
String arch = System.getProperty("os.arch");
diff --git a/core/src/main/java/hudson/node_monitors/ClockMonitor.java b/core/src/main/java/hudson/node_monitors/ClockMonitor.java
index 4352ec9bd4..cf3f025997 100644
--- a/core/src/main/java/hudson/node_monitors/ClockMonitor.java
+++ b/core/src/main/java/hudson/node_monitors/ClockMonitor.java
@@ -25,6 +25,7 @@ package hudson.node_monitors;
import hudson.model.Computer;
import hudson.model.Node;
+import hudson.remoting.Callable;
import hudson.util.ClockDifference;
import hudson.Extension;
import org.kohsuke.stapler.StaplerRequest;
@@ -46,11 +47,12 @@ public class ClockMonitor extends NodeMonitor {
}
@Extension
- public static final AbstractNodeMonitorDescriptor DESCRIPTOR = new AbstractNodeMonitorDescriptor() {
- protected ClockDifference monitor(Computer c) throws IOException, InterruptedException {
+ public static final AbstractNodeMonitorDescriptor DESCRIPTOR = new AbstractAsyncNodeMonitorDescriptor() {
+ @Override
+ protected Callable createCallable(Computer c) {
Node n = c.getNode();
if(n==null) return null;
- return n.getClockDifference();
+ return n.getClockDifferenceCallable();
}
public String getDisplayName() {
diff --git a/core/src/main/java/hudson/node_monitors/DiskSpaceMonitor.java b/core/src/main/java/hudson/node_monitors/DiskSpaceMonitor.java
index bc43661e2c..1177652124 100644
--- a/core/src/main/java/hudson/node_monitors/DiskSpaceMonitor.java
+++ b/core/src/main/java/hudson/node_monitors/DiskSpaceMonitor.java
@@ -27,6 +27,7 @@ import hudson.Extension;
import hudson.FilePath;
import hudson.Functions;
import hudson.model.Computer;
+import hudson.remoting.Callable;
import jenkins.model.Jenkins;
import hudson.node_monitors.DiskSpaceMonitorDescriptor.DiskSpace;
import org.kohsuke.stapler.DataBoundConstructor;
@@ -64,11 +65,12 @@ public class DiskSpaceMonitor extends AbstractDiskSpaceMonitor {
return Messages.DiskSpaceMonitor_DisplayName();
}
- @Override protected DiskSpace monitor(Computer c) throws IOException, InterruptedException {
+ @Override
+ protected Callable createCallable(Computer c) {
FilePath p = c.getNode().getRootPath();
if(p==null) return null;
- return p.act(new GetUsableSpace());
+ return p.asCallableWith(new GetUsableSpace());
}
};
diff --git a/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java b/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java
index baf1fc4446..8f1fb84995 100644
--- a/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java
+++ b/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java
@@ -47,7 +47,7 @@ import org.kohsuke.stapler.export.Exported;
* @author Kohsuke Kawaguchi
* @since 1.520
*/
-public abstract class DiskSpaceMonitorDescriptor extends AbstractNodeMonitorDescriptor {
+public abstract class DiskSpaceMonitorDescriptor extends AbstractAsyncNodeMonitorDescriptor {
/**
* Value object that represents the disk space.
*/
diff --git a/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java b/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java
index b8b38e9283..db8271f4bd 100644
--- a/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java
+++ b/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java
@@ -29,12 +29,16 @@ import hudson.slaves.OfflineCause;
import hudson.model.Computer;
import hudson.remoting.Callable;
import hudson.remoting.Future;
+import hudson.util.ClockDifference;
import hudson.util.TimeUnit2;
import hudson.util.IOException2;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
import java.io.IOException;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -49,33 +53,33 @@ import org.kohsuke.stapler.export.ExportedBean;
*/
public class ResponseTimeMonitor extends NodeMonitor {
@Extension
- public static final AbstractNodeMonitorDescriptor DESCRIPTOR = new AbstractNodeMonitorDescriptor() {
- protected Data monitor(Computer c) throws IOException, InterruptedException {
- Data old = get(c);
- Data d;
+ public static final AbstractNodeMonitorDescriptor DESCRIPTOR = new AbstractAsyncNodeMonitorDescriptor() {
+ @Override
+ protected Callable createCallable(Computer c) {
+ return new Step1(get(c));
+ }
- long start = System.nanoTime();
- Future f = c.getChannel().callAsync(new NoopTask());
- try {
- f.get(TIMEOUT, TimeUnit.MILLISECONDS);
- long end = System.nanoTime();
- d = new Data(old,TimeUnit2.NANOSECONDS.toMillis(end-start));
- } catch (ExecutionException e) {
- throw new IOException2(e.getCause()); // I don't think this is possible
- } catch (TimeoutException e) {
- // special constant to indicate that the processing timed out.
- d = new Data(old,-1L);
- }
+ @Override
+ protected Map monitor() throws InterruptedException {
+ Map base = super.monitor();
+ for (Entry e : base.entrySet()) {
+ Computer c = e.getKey();
+ Data d = e.getValue();
+ if (d ==null) {
+ // if we failed to monitor, put in the special value that indicates a failure
+ e.setValue(d=new Data(get(c),-1L));
+ }
- if(d.hasTooManyTimeouts() && !isIgnored()) {
- // unlike other monitors whose failure still allow us to communicate with the slave,
- // the failure in this monitor indicates that we are just unable to make any requests
- // to this slave. So we should severe the connection, as opposed to marking it temporarily
- // off line, which still keeps the underlying channel open.
- c.disconnect(d);
- LOGGER.warning(Messages.ResponseTimeMonitor_MarkedOffline(c.getName()));
+ if(d.hasTooManyTimeouts() && !isIgnored()) {
+ // unlike other monitors whose failure still allow us to communicate with the slave,
+ // the failure in this monitor indicates that we are just unable to make any requests
+ // to this slave. So we should severe the connection, as opposed to marking it temporarily
+ // off line, which still keeps the underlying channel open.
+ c.disconnect(d);
+ LOGGER.warning(Messages.ResponseTimeMonitor_MarkedOffline(c.getName()));
+ }
}
- return d;
+ return base;
}
public String getDisplayName() {
@@ -88,11 +92,63 @@ public class ResponseTimeMonitor extends NodeMonitor {
}
};
+ private static final class Step1 implements Callable {
+ private Data cur;
+
+ private Step1(Data cur) {
+ this.cur = cur;
+ }
+
+ public Data call() {
+ // this method must be being invoked locally, which means the roundtrip time is zero and zero forever
+ return new Data(cur,0);
+ }
+
+ private Object writeReplace() {
+ return new Step2(cur);
+ }
+
+ private static final long serialVersionUID = 1L;
+ }
+
+ private static final class Step2 implements Callable {
+ private final Data cur;
+ private final long start = System.nanoTime();
+
+ public Step2(Data cur) {
+ this.cur = cur;
+ }
+
+ public Step3 call() {
+ // this method must be being invoked locally, which means the roundtrip time is zero and zero forever
+ return new Step3(cur,start);
+ }
+
+ private static final long serialVersionUID = 1L;
+ }
+
+ private static final class Step3 implements Serializable {
+ private final Data cur;
+ private final long start;
+
+ private Step3(Data cur, long start) {
+ this.cur = cur;
+ this.start = start;
+ }
+
+ private Object readResolve() {
+ long end = System.nanoTime();
+ return new Data(cur,TimeUnit2.NANOSECONDS.toMillis(end-start));
+ }
+
+ private static final long serialVersionUID = 1L;
+ }
+
/**
* Immutable representation of the monitoring data.
*/
@ExportedBean
- public static final class Data extends OfflineCause {
+ public static final class Data extends OfflineCause implements Serializable {
/**
* Record of the past 5 times. -1 if time out. Otherwise in milliseconds.
* Old ones first.
@@ -153,12 +209,6 @@ public class ResponseTimeMonitor extends NodeMonitor {
return Util.wrapToErrorSpan(Messages.ResponseTimeMonitor_TimeOut(fc));
return getAverage()+"ms";
}
- }
-
- private static class NoopTask implements Callable {
- public String call() {
- return null;
- }
private static final long serialVersionUID = 1L;
}
diff --git a/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java b/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java
index b2edc41fc9..404aa3f779 100644
--- a/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java
+++ b/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java
@@ -78,9 +78,10 @@ public class SwapSpaceMonitor extends NodeMonitor {
}
@Extension
- public static final AbstractNodeMonitorDescriptor DESCRIPTOR = new AbstractNodeMonitorDescriptor() {
- protected MemoryUsage monitor(Computer c) throws IOException, InterruptedException {
- return c.getChannel().call(new MonitorTask());
+ public static final AbstractNodeMonitorDescriptor DESCRIPTOR = new AbstractAsyncNodeMonitorDescriptor() {
+ @Override
+ protected MonitorTask createCallable(Computer c) {
+ return new MonitorTask();
}
public String getDisplayName() {
diff --git a/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java b/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java
index f6919f56d3..05407d318f 100644
--- a/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java
+++ b/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java
@@ -28,6 +28,7 @@ import hudson.FilePath;
import hudson.FilePath.FileCallable;
import hudson.Functions;
import hudson.model.Computer;
+import hudson.remoting.Callable;
import jenkins.model.Jenkins;
import hudson.node_monitors.DiskSpaceMonitorDescriptor.DiskSpace;
import hudson.remoting.VirtualChannel;
@@ -66,11 +67,12 @@ public class TemporarySpaceMonitor extends AbstractDiskSpaceMonitor {
return Messages.TemporarySpaceMonitor_DisplayName();
}
- @Override protected DiskSpace monitor(Computer c) throws IOException, InterruptedException {
+ @Override
+ protected Callable createCallable(Computer c) {
FilePath p = c.getNode().getRootPath();
if(p==null) return null;
- return p.act(new GetTempSpace());
+ return p.asCallableWith(new GetTempSpace());
}
};
diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java
index dcd190a7e7..e155381e24 100755
--- a/core/src/main/java/jenkins/model/Jenkins.java
+++ b/core/src/main/java/jenkins/model/Jenkins.java
@@ -126,6 +126,7 @@ import hudson.lifecycle.Lifecycle;
import hudson.logging.LogRecorderManager;
import hudson.lifecycle.RestartNotSupportedException;
import hudson.markup.RawHtmlMarkupFormatter;
+import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.LocalChannel;
import hudson.remoting.VirtualChannel;
@@ -1960,6 +1961,15 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro
return ClockDifference.ZERO;
}
+ @Override
+ public Callable getClockDifferenceCallable() {
+ return new Callable() {
+ public ClockDifference call() throws IOException {
+ return new ClockDifference(0);
+ }
+ };
+ }
+
/**
* For binding {@link LogRecorderManager} to "/log".
* Everything below here is admin-only, so do the check here.
diff --git a/core/src/test/java/hudson/slaves/NodeListTest.java b/core/src/test/java/hudson/slaves/NodeListTest.java
index 29b1d96ba5..b802ce372d 100644
--- a/core/src/test/java/hudson/slaves/NodeListTest.java
+++ b/core/src/test/java/hudson/slaves/NodeListTest.java
@@ -24,6 +24,7 @@
package hudson.slaves;
+import hudson.remoting.Callable;
import jenkins.model.Jenkins;
import junit.framework.TestCase;
import hudson.model.Node;
@@ -98,7 +99,8 @@ public class NodeListTest extends TestCase {
throw new UnsupportedOperationException();
}
- public ClockDifference getClockDifference() throws IOException, InterruptedException {
+ @Override
+ public Callable getClockDifferenceCallable() {
throw new UnsupportedOperationException();
}