package jenkins.slaves.restarter; import hudson.Extension; import hudson.model.Computer; import hudson.model.TaskListener; import hudson.remoting.Engine; import hudson.remoting.EngineListener; import hudson.remoting.EngineListenerAdapter; import hudson.remoting.VirtualChannel; import hudson.slaves.ComputerListener; import jenkins.model.Jenkins.MasterComputer; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.logging.Logger; import static java.util.logging.Level.*; import jenkins.security.MasterToSlaveCallable; /** * Actual agent restart logic. * * <p> * Use {@link ComputerListener} to install {@link EngineListener}, which in turn gets executed when * the agent gets disconnected. * * @author Kohsuke Kawaguchi */ @Extension public class JnlpSlaveRestarterInstaller extends ComputerListener implements Serializable { @Override public void onOnline(final Computer c, final TaskListener listener) throws IOException, InterruptedException { MasterComputer.threadPoolForRemoting.submit(new java.util.concurrent.Callable<Void>() { @Override public Void call() throws Exception { install(c, listener); return null; } }); } private void install(Computer c, TaskListener listener) { try { final List<SlaveRestarter> restarters = new ArrayList<SlaveRestarter>(SlaveRestarter.all()); VirtualChannel ch = c.getChannel(); if (ch==null) return; // defensive check List<SlaveRestarter> effective = ch.call(new MasterToSlaveCallable<List<SlaveRestarter>, IOException>() { public List<SlaveRestarter> call() throws IOException { Engine e = Engine.current(); if (e == null) return null; // not running under Engine try { Engine.class.getMethod("addListener", EngineListener.class); } catch (NoSuchMethodException _) { return null; // running with older version of remoting that doesn't support adding listener } // filter out ones that doesn't apply for (Iterator<SlaveRestarter> itr = restarters.iterator(); itr.hasNext(); ) { SlaveRestarter r = itr.next(); if (!r.canWork()) itr.remove(); } e.addListener(new EngineListenerAdapter() { @Override public void onReconnect() { try { for (SlaveRestarter r : restarters) { try { LOGGER.info("Restarting agent via "+r); r.restart(); } catch (Exception x) { LOGGER.log(SEVERE, "Failed to restart agent with "+r, x); } } } finally { // if we move on to the reconnection without restart, // don't let the current implementations kick in when the agent loses connection again restarters.clear(); } } }); return restarters; } }); LOGGER.log(FINE, "Effective SlaveRestarter on {0}: {1}", new Object[] {c.getName(), effective}); } catch (Throwable e) { e.printStackTrace(listener.error("Failed to install restarter")); } } private static final Logger LOGGER = Logger.getLogger(JnlpSlaveRestarterInstaller.class.getName()); private static final long serialVersionUID = 1L; }