PageRenderTime 48ms CodeModel.GetById 11ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/hudson-core/src/main/java/hudson/slaves/CommandLauncher.java

http://github.com/hudson/hudson
Java | 184 lines | 118 code | 24 blank | 42 comment | 10 complexity | a6758eca02f8ee82f2b93298ab294df4 MD5 | raw file
  1/*
  2 * The MIT License
  3 * 
  4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Stephen Connolly
  5 * 
  6 * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 * of this software and associated documentation files (the "Software"), to deal
  8 * in the Software without restriction, including without limitation the rights
  9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10 * copies of the Software, and to permit persons to whom the Software is
 11 * furnished to do so, subject to the following conditions:
 12 * 
 13 * The above copyright notice and this permission notice shall be included in
 14 * all copies or substantial portions of the Software.
 15 * 
 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22 * THE SOFTWARE.
 23 */
 24package hudson.slaves;
 25
 26import hudson.EnvVars;
 27import hudson.Util;
 28import hudson.Extension;
 29import hudson.model.Descriptor;
 30import hudson.model.Hudson;
 31import hudson.model.TaskListener;
 32import hudson.remoting.Channel;
 33import hudson.util.StreamCopyThread;
 34import hudson.util.FormValidation;
 35import hudson.util.ProcessTree;
 36
 37import java.io.IOException;
 38import java.util.Date;
 39import java.util.logging.Level;
 40import java.util.logging.Logger;
 41
 42import org.kohsuke.stapler.DataBoundConstructor;
 43import org.kohsuke.stapler.QueryParameter;
 44
 45/**
 46 * {@link ComputerLauncher} through a remote login mechanism like ssh/rsh.
 47 *
 48 * @author Stephen Connolly
 49 * @author Kohsuke Kawaguchi
 50*/
 51public class CommandLauncher extends ComputerLauncher {
 52
 53    /**
 54     * Command line to launch the agent, like
 55     * "ssh myslave java -jar /path/to/hudson-remoting.jar"
 56     */
 57    private final String agentCommand;
 58    
 59    /**
 60     * Optional environment variables to add to the current environment. Can be null.
 61     */
 62    private final EnvVars env;
 63
 64    @DataBoundConstructor
 65    public CommandLauncher(String command) {
 66        this(command, null);
 67    }
 68    
 69    public CommandLauncher(String command, EnvVars env) {
 70    	this.agentCommand = command;
 71    	this.env = env;
 72    }
 73
 74    public String getCommand() {
 75        return agentCommand;
 76    }
 77
 78    /**
 79     * Gets the formatted current time stamp.
 80     */
 81    private static String getTimestamp() {
 82        return String.format("[%1$tD %1$tT]", new Date());
 83    }
 84
 85    @Override
 86    public void launch(SlaveComputer computer, final TaskListener listener) {
 87        EnvVars _cookie = null;
 88        Process _proc = null;
 89        try {
 90            listener.getLogger().println(hudson.model.Messages.Slave_Launching(getTimestamp()));
 91            if(getCommand().trim().length()==0) {
 92                listener.getLogger().println(Messages.CommandLauncher_NoLaunchCommand());
 93                return;
 94            }
 95            listener.getLogger().println("$ " + getCommand());
 96
 97            ProcessBuilder pb = new ProcessBuilder(Util.tokenize(getCommand()));
 98            final EnvVars cookie = _cookie = EnvVars.createCookie();
 99            pb.environment().putAll(cookie);
100
101            {// system defined variables
102                String rootUrl = Hudson.getInstance().getRootUrl();
103                if (rootUrl!=null) {
104                    pb.environment().put("HUDSON_URL", rootUrl);
105                    pb.environment().put("SLAVEJAR_URL", rootUrl+"/jnlpJars/slave.jar");
106                }
107            }
108
109            if (env != null) {
110            	pb.environment().putAll(env);
111            }
112            
113            final Process proc = _proc = pb.start();
114
115            // capture error information from stderr. this will terminate itself
116            // when the process is killed.
117            new StreamCopyThread("stderr copier for remote agent on " + computer.getDisplayName(),
118                    proc.getErrorStream(), listener.getLogger()).start();
119
120            computer.setChannel(proc.getInputStream(), proc.getOutputStream(), listener.getLogger(), new Channel.Listener() {
121                @Override
122                public void onClosed(Channel channel, IOException cause) {
123                    try {
124                        int exitCode = proc.exitValue();
125                        if (exitCode!=0) {
126                            listener.error("Process terminated with exit code "+exitCode);
127                        }
128                    } catch (IllegalThreadStateException e) {
129                        // hasn't terminated yet
130                    }
131
132                    try {
133                        ProcessTree.get().killAll(proc, cookie);
134                    } catch (InterruptedException e) {
135                        LOGGER.log(Level.INFO, "interrupted", e);
136                    }
137                }
138            });
139
140            LOGGER.info("slave agent launched for " + computer.getDisplayName());
141        } catch (InterruptedException e) {
142            e.printStackTrace(listener.error(Messages.ComputerLauncher_abortedLaunch()));
143        } catch (RuntimeException e) {
144            e.printStackTrace(listener.error(Messages.ComputerLauncher_unexpectedError()));
145        } catch (Error e) {
146            e.printStackTrace(listener.error(Messages.ComputerLauncher_unexpectedError()));
147        } catch (IOException e) {
148            Util.displayIOException(e, listener);
149
150            String msg = Util.getWin32ErrorMessage(e);
151            if (msg == null) {
152                msg = "";
153            } else {
154                msg = " : " + msg;
155            }
156            msg = hudson.model.Messages.Slave_UnableToLaunch(computer.getDisplayName(), msg);
157            LOGGER.log(Level.SEVERE, msg, e);
158            e.printStackTrace(listener.error(msg));
159
160            if(_proc!=null)
161                try {
162                    ProcessTree.get().killAll(_proc, _cookie);
163                } catch (InterruptedException x) {
164                    x.printStackTrace(listener.error(Messages.ComputerLauncher_abortedLaunch()));
165                }
166        }
167    }
168
169    private static final Logger LOGGER = Logger.getLogger(CommandLauncher.class.getName());
170
171    @Extension
172    public static class DescriptorImpl extends Descriptor<ComputerLauncher> {
173        public String getDisplayName() {
174            return Messages.CommandLauncher_displayName();
175        }
176
177        public FormValidation doCheckCommand(@QueryParameter String value) {
178            if(Util.fixEmptyAndTrim(value)==null)
179                return FormValidation.error("Command is empty");
180            else
181                return FormValidation.ok();
182        }
183    }
184}