/* * The MIT License * * Copyright 2014 CloudBees, Inc. * * 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 org.jenkinsci.plugins.durabletask; import hudson.FilePath; import hudson.Launcher; import hudson.Util; import hudson.util.LogTaskListener; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; /** * Defines how to control the execution of a task after it has started. * Expected to be XStream and Java serializable. */ public abstract class Controller implements Serializable { /** * Obtains any new task log output. * Could use a serializable field to keep track of how much output has been previously written. * @param workspace the workspace in use * @param sink where to send new log output * @return true if something was written and the controller should be resaved, false if everything is idle */ public abstract boolean writeLog(FilePath workspace, OutputStream sink) throws IOException, InterruptedException; /** * Checks whether the task has finished. * @param workspace the workspace in use * @param launcher a way to start processes * @return an exit code (zero is successful), or null if the task appears to still be running */ public @CheckForNull Integer exitStatus(FilePath workspace, Launcher launcher) throws IOException, InterruptedException { if (Util.isOverridden(Controller.class, getClass(), "exitStatus", FilePath.class)) { return exitStatus(workspace); } else { throw new AbstractMethodError("implement exitStatus(FilePath, Launcher)"); } } /** @deprecated use {@link #exitStatus(FilePath, Launcher)} instead */ public @CheckForNull Integer exitStatus(FilePath workspace) throws IOException, InterruptedException { return exitStatus(workspace, createLauncher(workspace)); } /** * Obtain the process output. * Intended for use after {@link #exitStatus(FilePath, Launcher)} has returned a non-null status. * The result is undefined if {@link DurableTask#captureOutput} was not called before launch; generally an {@link IOException} will result. * @param workspace the workspace in use * @param launcher a way to start processes * @return the output of the process as raw bytes (may be empty but not null) */ public @Nonnull byte[] getOutput(@Nonnull FilePath workspace, @Nonnull Launcher launcher) throws IOException, InterruptedException { throw new IOException("Did not implement getOutput in " + getClass().getName()); } /** * Tries to stop any running task. * @param workspace the workspace in use * @param launcher a way to start processes */ public void stop(FilePath workspace, Launcher launcher) throws IOException, InterruptedException { if (Util.isOverridden(Controller.class, getClass(), "stop", FilePath.class)) { stop(workspace); } else { throw new AbstractMethodError("implement stop(FilePath, Launcher)"); } } /** @deprecated use {@link #stop(FilePath, Launcher)} instead */ public void stop(FilePath workspace) throws IOException, InterruptedException { stop(workspace, createLauncher(workspace)); } private static Launcher createLauncher(FilePath workspace) throws IOException, InterruptedException { return workspace.createLauncher(new LogTaskListener(Logger.getLogger(Controller.class.getName()), Level.FINE)); } /** * Cleans up after a task is done. * Should delete any temporary files created by {@link DurableTask#launch}. * @param workspace the workspace in use */ public abstract void cleanup(FilePath workspace) throws IOException, InterruptedException; /** * Should be overridden to provide specific information about the status of an external process, for diagnostic purposes. * @return {@link #toString} by default */ public String getDiagnostics(FilePath workspace, Launcher launcher) throws IOException, InterruptedException { return toString(); } private static final long serialVersionUID = 1L; }