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.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 agents concurrently and asynchronously. * * @param <T> * represents the the result of the monitoring. * @author Kohsuke Kawaguchi */ public abstract class AbstractAsyncNodeMonitorDescriptor<T> extends AbstractNodeMonitorDescriptor<T> { 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<T,IOException> createCallable(Computer c); @Override protected T monitor(Computer c) throws IOException, InterruptedException { VirtualChannel ch = c.getChannel(); if (ch != null) { Callable<T,IOException> cc = createCallable(c); if (cc!=null) return ch.call(cc); } return null; } /** * Performs all monitoring concurrently. */ @Override protected Map<Computer, T> monitor() throws InterruptedException { Map<Computer,Future<T>> futures = new HashMap<Computer,Future<T>>(); for (Computer c : Jenkins.getInstance().getComputers()) { try { VirtualChannel ch = c.getChannel(); futures.put(c,null); // sentinel value if (ch!=null) { Callable<T, ?> 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<Computer,T> data = new HashMap<Computer,T>(); for (Entry<Computer, Future<T>> e : futures.entrySet()) { Computer c = e.getKey(); Future<T> 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; } private static final Logger LOGGER = Logger.getLogger(AbstractAsyncNodeMonitorDescriptor.class.getName()); }