package org.jenkinsci.plugins.ondemandslave;


import com.google.common.base.Strings;
import hudson.Extension;
import hudson.Functions;
import hudson.Launcher;
import hudson.model.Descriptor;
import hudson.model.TaskListener;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.DelegatingComputerLauncher;
import hudson.slaves.SlaveComputer;
import hudson.util.StreamTaskListener;
import jenkins.model.Jenkins;
import org.kohsuke.stapler.DataBoundConstructor;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Implements the custom logic for an on-demand slave, executing commands before connecting and after disconnecting
 */
public class OnDemandSlaveLauncher extends DelegatingComputerLauncher {

    private final String startScript;
    private final String stopScript;

    @DataBoundConstructor
    public OnDemandSlaveLauncher(ComputerLauncher launcher,
                          String startScript,
                          String stopScript) {
        super(launcher);
        this.startScript = startScript;
        this.stopScript = stopScript;
    }

    /**
     * Executes a command on the master node, with a bit of tracing.
     */
    private void execute(String command, TaskListener listener) {
        Jenkins jenkins = Jenkins.getInstance();

        if (jenkins == null) {
            listener.getLogger().println("Jenkins is not ready... doing nothing");
            return;
        }

        if (Strings.isNullOrEmpty(command)) {
            listener.getLogger().println("No command to be executed for this on-demand slave.");
            return;
        }

        try {
            Launcher launcher = jenkins.getRootPath().createLauncher(listener);
            launcher.launch().cmdAsSingleString(command).stdout(listener).join();
        } catch (Exception e) {
            listener.getLogger().println("Failed executing command '" + command + "'");
            e.printStackTrace(listener.getLogger());
        }

    }

    /*
     * Getters for Jelly
     */
    public String getStartScript() {
        return startScript;
    }

    public String getStopScript() {
        return stopScript;
    }

    /*
     *  Delegated methods that plug the additional logic for on-demand slaves
     */

    @Override
    public void launch(SlaveComputer computer, TaskListener listener) throws IOException, InterruptedException {
        execute(startScript, listener);
        super.launch(computer, listener);
    }

    @Override
    public void afterDisconnect(SlaveComputer computer, TaskListener listener) {
        super.afterDisconnect(computer, listener);
        execute(stopScript, listener);
    }

    @Extension
    public static class DescriptorImpl extends Descriptor<ComputerLauncher> {

        public String getDisplayName() {
            return "Start and stop this node on-demand";
        }

        /**
         * Returns the applicable nested computer launcher types.
         * The default implementation avoids all delegating descriptors, as that creates infinite recursion.
         */
        public List<Descriptor<ComputerLauncher>> getApplicableDescriptors() {
            List<Descriptor<ComputerLauncher>> r = new ArrayList<>();
            for (Descriptor<ComputerLauncher> d : Functions.getComputerLauncherDescriptors()) {
                if (DelegatingComputerLauncher.class.isAssignableFrom(d.getKlass().toJavaClass()))  continue;
                r.add(d);
            }
            return r;
        }
    }

}

@@ -97,66 +97,9 @@
 son.
-Extension;%0Aimport hudson.Functions;%0Aimport hudson.Launcher
+*
 ;%0Aim
@@ -304,31 +304,19 @@
 son.
-util.StreamTaskListener
+tasks.Shell
 ;%0Aim
@@ -412,78 +412,19 @@
 .io.
-BufferedReader;%0Aimport java.io.IOException;%0Aimport java.io.PrintStream
+IOException
 ;%0Aim
@@ -542,23 +542,22 @@
 ecuting 
-command
+script
 s before
@@ -1060,23 +1060,22 @@
 cutes a 
-command
+script
  on the 
@@ -1150,23 +1150,22 @@
 (String 
-command
+script
 , TaskLi
@@ -1172,32 +1172,73 @@
 stener listener)
+ throws IOException, InterruptedException
  %7B%0A        Jenki
@@ -1453,23 +1453,22 @@
 OrEmpty(
-command
+script
 )) %7B%0A   
@@ -1509,23 +1509,22 @@
 tln(%22No 
-command
+script
  to be e
@@ -1595,22 +1595,8 @@
  %7D%0A%0A
-        try %7B%0A
     
@@ -1687,51 +1687,233 @@
     
-launcher.launch().cmdAsSingleString(command
+Shell shell = new Shell(script);%0A            FilePath root = jenkins.getRootPath();%0A            FilePath scriptFile = shell.createScriptFile(root);%0A            int r = launcher.launch().cmds(shell.buildCommandLine(scriptFile)
 ).st
@@ -1935,24 +1935,29 @@
 join();%0A
+%0A    
         
 %7D catch 
@@ -1952,116 +1952,123 @@
     
-%7D catch (Exception e) %7B%0A            listener.getLogger().println(%22Failed executing command '%22 + command
+if (r != 0) %7B%0A                throw new AbortException(%22Script failed with return code %22 + Integer.toString(r)
  + %22
-'
+.
 %22);%0A
@@ -2082,59 +2082,86 @@
     
+ 
- e.printStackTrace(listener.getLogger());%0A        %7D
+%7D%0A%0A            listener.getLogger().println(%22Script executed successfully.%22);
 %0A%0A  
@@ -2795,32 +2795,51 @@
 ter, listener);%0A
+%0A        try %7B%0A    
         execute(
@@ -2853,32 +2853,219 @@
 ipt, listener);%0A
+        %7D  catch (Exception e) %7B%0A            listener.getLogger().println(%22Failed executing script '%22 + stopScript + %22'.%22);%0A            e.printStackTrace(listener.getLogger());%0A        %7D%0A
     %7D%0A%0A    @Exte

Add possibility to execute scripts of multiple lines by leveraging the Shell class