PageRenderTime 49ms CodeModel.GetById 13ms app.highlight 28ms RepoModel.GetById 2ms app.codeStats 0ms

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

http://github.com/hudson/hudson
Java | 214 lines | 98 code | 18 blank | 98 comment | 1 complexity | 74c780c92cd6ed570ab6b0a5dd5be53b MD5 | raw file
  1/*
  2 * The MIT License
  3 *
  4 * Copyright (c) 2004-2009, Sun Microsystems, Inc.
  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.Launcher.LocalLauncher;
 27import hudson.Proc;
 28import hudson.FilePath;
 29import hudson.model.Computer;
 30import hudson.model.TaskListener;
 31import hudson.remoting.Channel;
 32import hudson.remoting.Launcher;
 33import hudson.remoting.SocketInputStream;
 34import hudson.remoting.SocketOutputStream;
 35import hudson.util.ClasspathBuilder;
 36import hudson.util.JVMBuilder;
 37import hudson.util.StreamCopyThread;
 38
 39import java.io.BufferedInputStream;
 40import java.io.BufferedOutputStream;
 41import java.io.IOException;
 42import java.io.InputStream;
 43import java.io.OutputStream;
 44import java.net.InetSocketAddress;
 45import java.net.ServerSocket;
 46import java.net.Socket;
 47import java.util.Map;
 48import java.util.concurrent.ExecutorService;
 49import java.util.logging.Level;
 50import java.util.logging.Logger;
 51
 52/**
 53 * Various convenient subtype of {@link Channel}s.
 54 * 
 55 * @author Kohsuke Kawaguchi
 56 */
 57public class Channels {
 58    /**
 59     * @deprecated since 2009-04-13.
 60     *      Use {@link #forProcess(String, ExecutorService, InputStream, OutputStream, OutputStream, Proc)}
 61     */
 62    public static Channel forProcess(String name, ExecutorService execService, InputStream in, OutputStream out, Proc proc) throws IOException {
 63        return forProcess(name,execService,in,out,null,proc);
 64    }
 65
 66    /**
 67     * Creates a channel that wraps a remote process, so that when we shut down the connection
 68     * we kill the process.
 69     */
 70    public static Channel forProcess(String name, ExecutorService execService, InputStream in, OutputStream out, OutputStream header, final Proc proc) throws IOException {
 71        return new Channel(name, execService, in, out, header) {
 72            /**
 73             * Kill the process when the channel is severed.
 74             */
 75            @Override
 76            protected synchronized void terminate(IOException e) {
 77                super.terminate(e);
 78                try {
 79                    proc.kill();
 80                } catch (IOException x) {
 81                    // we are already in the error recovery mode, so just record it and move on
 82                    LOGGER.log(Level.INFO, "Failed to terminate the severed connection",x);
 83                } catch (InterruptedException x) {
 84                    // process the interrupt later
 85                    Thread.currentThread().interrupt();
 86                }
 87            }
 88
 89            @Override
 90            public synchronized void close() throws IOException {
 91                super.close();
 92                // wait for the child process to complete
 93                try {
 94                    proc.join();
 95                } catch (InterruptedException e) {
 96                    // process the interrupt later
 97                    Thread.currentThread().interrupt();
 98                }
 99            }
100        };
101    }
102
103    public static Channel forProcess(String name, ExecutorService execService, final Process proc, OutputStream header) throws IOException {
104        final Thread thread = new StreamCopyThread(name + " stderr", proc.getErrorStream(), header);
105        thread.start();
106
107        return new Channel(name, execService, proc.getInputStream(), proc.getOutputStream(), header) {
108            /**
109             * Kill the process when the channel is severed.
110             */
111            @Override
112            protected synchronized void terminate(IOException e) {
113                super.terminate(e);
114                proc.destroy();
115                // the stderr copier should exit by itself
116            }
117
118            @Override
119            public synchronized void close() throws IOException {
120                super.close();
121                // wait for Maven to complete
122                try {
123                    proc.waitFor();
124                    thread.join();
125                } catch (InterruptedException e) {
126                    // process the interrupt later
127                    Thread.currentThread().interrupt();
128                }
129            }
130        };
131    }
132
133    /**
134     * Launches a new JVM with the given classpath and system properties, establish a communication channel,
135     * and return a {@link Channel} to it.
136     *
137     * @param displayName
138     *      Human readable name of what this JVM represents. For example "Selenium grid" or "Hadoop".
139     *      This token is used for messages to {@code listener}.
140     * @param listener
141     *      The progress of the launcher and the failure information will be sent here. Must not be null.
142     * @param workDir
143     *      If non-null, the new JVM will have this directory as the working directory. This must be a local path.
144     * @param classpath
145     *      The classpath of the new JVM. Can be null if you just need {@code slave.jar} (and everything else
146     *      can be sent over the channel.) But if you have jars that are known to be necessary by the new JVM,
147     *      setting it here will improve the classloading performance (by avoiding remote class file transfer.)
148     *      Classes in this classpath will also take precedence over any other classes that's sent via the channel
149     *      later, so it's also useful for making sure you get the version of the classes you want.
150     * @param systemProperties
151     *      If the new JVM should have a certain system properties set. Can be null.
152     *
153     * @return
154     *      never null
155     * @since 1.300
156     */
157    public static Channel newJVM(String displayName, TaskListener listener, FilePath workDir, ClasspathBuilder classpath, Map<String,String> systemProperties) throws IOException {
158        JVMBuilder vmb = new JVMBuilder();
159        vmb.systemProperties(systemProperties);
160
161        return newJVM(displayName,listener,vmb,workDir,classpath);
162    }
163
164    /**
165     * Launches a new JVM with the given classpath, establish a communication channel,
166     * and return a {@link Channel} to it.
167     *
168     * @param displayName
169     *      Human readable name of what this JVM represents. For example "Selenium grid" or "Hadoop".
170     *      This token is used for messages to {@code listener}.
171     * @param listener
172     *      The progress of the launcher and the failure information will be sent here. Must not be null.
173     * @param workDir
174     *      If non-null, the new JVM will have this directory as the working directory. This must be a local path.
175     * @param classpath
176     *      The classpath of the new JVM. Can be null if you just need {@code slave.jar} (and everything else
177     *      can be sent over the channel.) But if you have jars that are known to be necessary by the new JVM,
178     *      setting it here will improve the classloading performance (by avoiding remote class file transfer.)
179     *      Classes in this classpath will also take precedence over any other classes that's sent via the channel
180     *      later, so it's also useful for making sure you get the version of the classes you want.
181     * @param vmb
182     *      A partially configured {@link JVMBuilder} that allows the caller to fine-tune the launch parameter.
183     *
184     * @return
185     *      never null
186     * @since 1.361
187     */
188    public static Channel newJVM(String displayName, TaskListener listener, JVMBuilder vmb, FilePath workDir, ClasspathBuilder classpath) throws IOException {
189        ServerSocket serverSocket = new ServerSocket();
190        serverSocket.bind(new InetSocketAddress("localhost",0));
191        serverSocket.setSoTimeout(10*1000);
192
193        // use -cp + FQCN instead of -jar since remoting.jar can be rebundled (like in the case of the swarm plugin.)
194        vmb.classpath().addJarOf(Channel.class);
195        vmb.mainClass(Launcher.class);
196
197        if(classpath!=null)
198            vmb.args().add("-cp").add(classpath);
199        vmb.args().add("-connectTo","localhost:"+serverSocket.getLocalPort());
200
201        listener.getLogger().println("Starting "+displayName);
202        Proc p = vmb.launch(new LocalLauncher(listener)).stdout(listener).pwd(workDir).start();
203
204        Socket s = serverSocket.accept();
205        serverSocket.close();
206
207        return forProcess("Channel to "+displayName, Computer.threadPoolForRemoting,
208                new BufferedInputStream(new SocketInputStream(s)),
209                new BufferedOutputStream(new SocketOutputStream(s)),null,p);
210    }
211
212
213    private static final Logger LOGGER = Logger.getLogger(Channels.class.getName());
214}