PageRenderTime 80ms CodeModel.GetById 15ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 0ms

/hudson-remoting/src/main/java/hudson/remoting/Channel.java

http://github.com/hudson/hudson
Java | 1123 lines | 528 code | 105 blank | 490 comment | 67 complexity | 2d187486d957ee04a194323983339552 MD5 | raw file
   1/*
   2 * The MIT License
   3 * 
   4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
   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.remoting;
  25
  26import hudson.remoting.ExportTable.ExportList;
  27import hudson.remoting.PipeWindow.Key;
  28import hudson.remoting.PipeWindow.Real;
  29import hudson.remoting.forward.ForwarderFactory;
  30import hudson.remoting.forward.ListeningPort;
  31import hudson.remoting.forward.PortForwarder;
  32import java.io.EOFException;
  33import java.io.IOException;
  34import java.io.InputStream;
  35import java.io.ObjectInputStream;
  36import java.io.ObjectOutputStream;
  37import java.io.OutputStream;
  38import java.io.PrintWriter;
  39import java.io.Serializable;
  40import java.io.UnsupportedEncodingException;
  41import java.lang.ref.WeakReference;
  42import java.net.URL;
  43import java.util.Collections;
  44import java.util.Hashtable;
  45import java.util.Map;
  46import java.util.Vector;
  47import java.util.WeakHashMap;
  48import java.util.concurrent.ExecutionException;
  49import java.util.concurrent.Executor;
  50import java.util.concurrent.ExecutorService;
  51import java.util.concurrent.Executors;
  52import java.util.concurrent.ThreadFactory;
  53import java.util.concurrent.atomic.AtomicInteger;
  54import java.util.concurrent.atomic.AtomicLong;
  55import java.util.logging.Level;
  56import java.util.logging.Logger;
  57
  58/**
  59 * Represents a communication channel to the remote peer.
  60 * <p/>
  61 * <p/>
  62 * A {@link Channel} is a mechanism for two JVMs to communicate over
  63 * bi-directional {@link InputStream}/{@link OutputStream} pair.
  64 * {@link Channel} represents an endpoint of the stream, and thus
  65 * two {@link Channel}s are always used in a pair.
  66 * <p/>
  67 * <p/>
  68 * Communication is established as soon as two {@link Channel} instances
  69 * are created at the end fo the stream pair
  70 * until the stream is terminated via {@link #close()}.
  71 * <p/>
  72 * <p/>
  73 * The basic unit of remoting is an executable {@link Callable} object.
  74 * An application can create a {@link Callable} object, and execute it remotely
  75 * by using the {@link #call(Callable)} method or {@link #callAsync(Callable)} method.
  76 * <p/>
  77 * <p/>
  78 * In this sense, {@link Channel} is a mechanism to delegate/offload computation
  79 * to other JVMs and somewhat like an agent system. This is bit different from
  80 * remoting technologies like CORBA or web services, where the server exposes a
  81 * certain functionality that clients invoke.
  82 * <p/>
  83 * <p/>
  84 * {@link Callable} object, as well as the return value / exceptions,
  85 * are transported by using Java serialization. All the necessary class files
  86 * are also shipped over {@link Channel} on-demand, so there's no need to
  87 * pre-deploy such classes on both JVMs.
  88 * <p/>
  89 * <p/>
  90 * <h2>Implementor's Note</h2>
  91 * <p/>
  92 * {@link Channel} builds its features in a layered model. Its higher-layer
  93 * features are built on top of its lower-layer features, and they
  94 * are called layer-0, layer-1, etc.
  95 * <p/>
  96 * <ul>
  97 * <li>
  98 * <b>Layer 0</b>:
  99 * See {@link Command} for more details. This is for higher-level features,
 100 * and not likely useful for applications directly.
 101 * <li>
 102 * <b>Layer 1</b>:
 103 * See {@link Request} for more details. This is for higher-level features,
 104 * and not likely useful for applications directly.
 105 * </ul>
 106 *
 107 * @author Kohsuke Kawaguchi, Winston Prakash (bug fixes)
 108 */
 109public class Channel implements VirtualChannel, IChannel {
 110    private final ObjectInputStream ois;
 111    private final ObjectOutputStream oos;
 112    /**
 113     * Human readable description of where this channel is connected to. Used during diagnostic output
 114     * and error reports.
 115     */
 116    private final String name;
 117    /*package*/ final boolean isRestricted;
 118    /*package*/ final ExecutorService executor;
 119
 120    /**
 121     * If non-null, the incoming link is already shut down,
 122     * and reader is already terminated. The {@link Throwable} object indicates why the outgoing channel
 123     * was closed.
 124     */
 125    private volatile Throwable inClosed = null;
 126    /**
 127     * If non-null, the outgoing link is already shut down,
 128     * and no command can be sent. The {@link Throwable} object indicates why the outgoing channel
 129     * was closed.
 130     */
 131    private volatile Throwable outClosed = null;
 132
 133    /*package*/ final Map<Integer, Request<?, ?>> pendingCalls = new Hashtable<Integer, Request<?, ?>>();
 134
 135    /**
 136     * Records the {@link Request}s being executed on this channel, sent by the remote peer.
 137     */
 138    /*package*/ final Map<Integer, Request<?, ?>> executingCalls =
 139        Collections.synchronizedMap(new Hashtable<Integer, Request<?, ?>>());
 140
 141    /**
 142     * {@link ClassLoader}s that are proxies of the remote classloaders.
 143     */
 144    /*package*/ final ImportedClassLoaderTable importedClassLoaders = new ImportedClassLoaderTable(this);
 145
 146    /**
 147     * Objects exported via {@link #export(Class, Object)}.
 148     */
 149    private final ExportTable<Object> exportedObjects = new ExportTable<Object>();
 150
 151    /**
 152     * {@link PipeWindow}s keyed by their OIDs (of the OutputStream exported by the other side.)
 153     * <p/>
 154     * <p/>
 155     * To make the GC of {@link PipeWindow} automatic, the use of weak references here are tricky.
 156     * A strong reference to {@link PipeWindow} is kept from {@link ProxyOutputStream}, and
 157     * this is the only strong reference. Thus while {@link ProxyOutputStream} is alive,
 158     * it keeps {@link PipeWindow} referenced, which in turn keeps its {@link PipeWindow.Real#key}
 159     * referenced, hence this map can be looked up by the OID. When the {@link ProxyOutputStream}
 160     * will be gone, the key is no longer strongly referenced, so it'll get cleaned up.
 161     * <p/>
 162     * <p/>
 163     * In some race condition situation, it might be possible for us to lose the tracking of the collect
 164     * window size. But as long as we can be sure that there's only one {@link PipeWindow} instance
 165     * per OID, it will only result in a temporary spike in the effective window size,
 166     * and therefore should be OK.
 167     */
 168    private final WeakHashMap<PipeWindow.Key, WeakReference<PipeWindow>> pipeWindows
 169        = new WeakHashMap<PipeWindow.Key, WeakReference<PipeWindow>>();
 170
 171    /**
 172     * Registered listeners.
 173     */
 174    private final Vector<Listener> listeners = new Vector<Listener>();
 175    private int gcCounter;
 176
 177    /**
 178     * Total number of nanoseconds spent for remote class loading.
 179     * <p/>
 180     * Remote code execution often results in classloading activity
 181     * (more precisely, when the remote peer requests some computation
 182     * on this channel, this channel often has to load necessary
 183     * classes from the remote peer.)
 184     * <p/>
 185     * This counter represents the total amount of time this channel
 186     * had to spend loading classes from the remote peer. The time
 187     * measurement doesn't include the time locally spent to actually
 188     * define the class (as the local classloading would have incurred
 189     * the same cost.)
 190     */
 191    public final AtomicLong classLoadingTime = new AtomicLong();
 192
 193    /**
 194     * Total counts of remote classloading activities. Used in a pair
 195     * with {@link #classLoadingTime}.
 196     */
 197    public final AtomicInteger classLoadingCount = new AtomicInteger();
 198
 199    /**
 200     * Total number of nanoseconds spent for remote resource loading.
 201     *
 202     * @see #classLoadingTime
 203     */
 204    public final AtomicLong resourceLoadingTime = new AtomicLong();
 205
 206    /**
 207     * Total count of remote resource loading.
 208     *
 209     * @see #classLoadingCount
 210     */
 211    public final AtomicInteger resourceLoadingCount = new AtomicInteger();
 212
 213    /**
 214     * Property bag that contains application-specific stuff.
 215     */
 216    private final Hashtable<Object, Object> properties = new Hashtable<Object, Object>();
 217
 218    /**
 219     * Proxy to the remote {@link Channel} object.
 220     */
 221    private IChannel remoteChannel;
 222
 223    /**
 224     * Capability of the remote {@link Channel}.
 225     */
 226    public final Capability remoteCapability;
 227
 228    /**
 229     * When did we receive any data from this slave the last time?
 230     * This can be used as a basis for detecting dead connections.
 231     * <p/>
 232     * Note that this doesn't include our sender side of the operation,
 233     * as successfully returning from {@link #send(Command)} doesn't mean
 234     * anything in terms of whether the underlying network was able to send
 235     * the data (for example, if the other end of a socket connection goes down
 236     * without telling us anything, the {@link SocketOutputStream#write(int)} will
 237     * return right away, and the socket only really times out after 10s of minutes.
 238     */
 239    private volatile long lastHeard;
 240
 241    /*package*/ final ExecutorService pipeWriter;
 242
 243
 244    /**
 245     * Communication mode.
 246     *
 247     * @since 1.161
 248     */
 249    public enum Mode {
 250        /**
 251         * Send binary data over the stream. Most efficient.
 252         */
 253        BINARY(new byte[]{0, 0, 0, 0}),
 254        /**
 255         * Send ASCII over the stream. Uses base64, so the efficiency goes down by 33%,
 256         * but this is useful where stream is binary-unsafe, such as telnet.
 257         */
 258        TEXT("<===[HUDSON TRANSMISSION BEGINS]===>") {
 259            @Override
 260            protected OutputStream wrap(OutputStream os) {
 261                return BinarySafeStream.wrap(os);
 262            }
 263
 264            @Override
 265            protected InputStream wrap(InputStream is) {
 266                return BinarySafeStream.wrap(is);
 267            }
 268        },
 269        /**
 270         * Let the remote peer decide the transmission mode and follow that.
 271         * Note that if both ends use NEGOTIATE, it will dead lock.
 272         */
 273        NEGOTIATE(new byte[0]);
 274
 275        /**
 276         * Preamble used to indicate the tranmission mode.
 277         * Because of the algorithm we use to detect the preamble,
 278         * the string cannot be any random string. For example,
 279         * if the preamble is "AAB", we'll fail to find a preamble
 280         * in "AAAB".
 281         */
 282        private final byte[] preamble;
 283
 284        Mode(String preamble) {
 285            try {
 286                this.preamble = preamble.getBytes("US-ASCII");
 287            } catch (UnsupportedEncodingException e) {
 288                throw new Error(e);
 289            }
 290        }
 291
 292        Mode(byte[] preamble) {
 293            this.preamble = preamble;
 294        }
 295
 296        protected OutputStream wrap(OutputStream os) {
 297            return os;
 298        }
 299
 300        protected InputStream wrap(InputStream is) {
 301            return is;
 302        }
 303    }
 304
 305    public Channel(String name, ExecutorService exec, InputStream is, OutputStream os) throws IOException {
 306        this(name, exec, Mode.BINARY, is, os, null);
 307    }
 308
 309    public Channel(String name, ExecutorService exec, Mode mode, InputStream is, OutputStream os) throws IOException {
 310        this(name, exec, mode, is, os, null);
 311    }
 312
 313    public Channel(String name, ExecutorService exec, InputStream is, OutputStream os, OutputStream header)
 314        throws IOException {
 315        this(name, exec, Mode.BINARY, is, os, header);
 316    }
 317
 318    public Channel(String name, ExecutorService exec, Mode mode, InputStream is, OutputStream os, OutputStream header)
 319        throws IOException {
 320        this(name, exec, mode, is, os, header, false);
 321    }
 322
 323    /**
 324     * Creates a new channel.
 325     *
 326     * @param name Human readable name of this channel. Used for debug/logging. Can be anything.
 327     * @param exec Commands sent from the remote peer will be executed by using this {@link Executor}.
 328     * @param mode The encoding to be used over the stream.
 329     * @param is Stream connected to the remote peer. It's the caller's responsibility to do
 330     * buffering on this stream, if that's necessary.
 331     * @param os Stream connected to the remote peer. It's the caller's responsibility to do
 332     * buffering on this stream, if that's necessary.
 333     * @param header If non-null, receive the portion of data in <tt>is</tt> before
 334     * the data goes into the "binary mode". This is useful
 335     * when the established communication channel might include some data that might
 336     * be useful for debugging/trouble-shooting.
 337     * @param restricted If true, this channel won't accept {@link Command}s that allow the remote end to execute arbitrary closures
 338     * --- instead they can only call methods on objects that are exported by this channel.
 339     * This also prevents the remote end from loading classes into JVM.
 340     * <p/>
 341     * Note that it still allows the remote end to deserialize arbitrary object graph
 342     * (provided that all the classes are already available in this JVM), so exactly how
 343     * safe the resulting behavior is is up to discussion.
 344     */
 345    public Channel(String name, ExecutorService exec, Mode mode, InputStream is, OutputStream os, OutputStream header,
 346                   boolean restricted) throws IOException {
 347        this(name, exec, mode, is, os, header, restricted, new Capability());
 348    }
 349
 350    /*package*/ Channel(String name, ExecutorService exec, Mode mode, InputStream is, OutputStream os,
 351                        OutputStream header, boolean restricted, Capability capability) throws IOException {
 352        this.name = name;
 353        this.executor = exec;
 354        this.isRestricted = restricted;
 355
 356        if (export(this, false) != 1) {
 357            throw new AssertionError(); // export number 1 is reserved for the channel itself
 358        }
 359        remoteChannel = RemoteInvocationHandler.wrap(this, 1, IChannel.class, false, false);
 360
 361        // write the magic preamble.
 362        // certain communication channel, such as forking JVM via ssh,
 363        // may produce some garbage at the beginning (for example a remote machine
 364        // might print some warning before the program starts outputting its own data.)
 365        //
 366        // so use magic preamble and discard all the data up to that to improve robustness.
 367
 368        capability.writePreamble(os);
 369
 370        ObjectOutputStream oos = null;
 371        if (mode != Mode.NEGOTIATE) {
 372            os.write(mode.preamble);
 373            oos = new ObjectOutputStream(mode.wrap(os));
 374            oos.flush();    // make sure that stream preamble is sent to the other end. avoids dead-lock
 375        }
 376
 377        {// read the input until we hit preamble
 378            Mode[] modes = {Mode.BINARY, Mode.TEXT};
 379            byte[][] preambles = new byte[][]{Mode.BINARY.preamble, Mode.TEXT.preamble, Capability.PREAMBLE};
 380            int[] ptr = new int[3];
 381            Capability cap = new Capability(
 382                0); // remote capacity that we obtained. If we don't hear from remote, assume no capability
 383
 384            while (true) {
 385                int ch = is.read();
 386                if (ch == -1) {
 387                    throw new EOFException("unexpected stream termination");
 388                }
 389
 390                for (int i = 0; i < preambles.length; i++) {
 391                    byte[] preamble = preambles[i];
 392                    if (preamble[ptr[i]] == ch) {
 393                        if (++ptr[i] == preamble.length) {
 394                            switch (i) {
 395                                case 0:
 396                                case 1:
 397                                    // transmission mode negotiation
 398                                    if (mode == Mode.NEGOTIATE) {
 399                                        // now we know what the other side wants, so send the consistent preamble
 400                                        mode = modes[i];
 401                                        os.write(mode.preamble);
 402                                        oos = new ObjectOutputStream(mode.wrap(os));
 403                                        oos.flush();
 404                                    } else {
 405                                        if (modes[i] != mode) {
 406                                            throw new IOException("Protocol negotiation failure");
 407                                        }
 408                                    }
 409                                    this.oos = oos;
 410                                    this.remoteCapability = cap;
 411                                    this.pipeWriter = createPipeWriter();
 412                                    this.ois = new ObjectInputStream(mode.wrap(is));
 413                                    new ReaderThread(name).start();
 414
 415                                    return;
 416                                case 2:
 417                                    cap = Capability.read(is);
 418                                    break;
 419                            }
 420                            ptr[i] = 0; // reset
 421                        }
 422                    } else {
 423                        // didn't match.
 424                        ptr[i] = 0;
 425                    }
 426                }
 427
 428                if (header != null) {
 429                    header.write(ch);
 430                }
 431            }
 432        }
 433    }
 434
 435    /**
 436     * Callback "interface" for changes in the state of {@link Channel}.
 437     */
 438    public static abstract class Listener {
 439        /**
 440         * When the channel was closed normally or abnormally due to an error.
 441         *
 442         * @param cause if the channel is closed abnormally, this parameter
 443         * represents an exception that has triggered it.
 444         * Otherwise null.
 445         */
 446        public void onClosed(Channel channel, IOException cause) {
 447        }
 448    }
 449
 450    /*package*/ boolean isOutClosed() {
 451        return outClosed != null;
 452    }
 453
 454    /**
 455     * Creates the {@link ExecutorService} for writing to pipes.
 456     * <p/>
 457     * <p/>
 458     * If the throttling is supported, use a separate thread to free up the main channel
 459     * reader thread (thus prevent blockage.) Otherwise let the channel reader thread do it,
 460     * which is the historical behaviour.
 461     */
 462    private ExecutorService createPipeWriter() {
 463        if (remoteCapability.supportsPipeThrottling()) {
 464            return Executors.newSingleThreadExecutor(new ThreadFactory() {
 465                public Thread newThread(Runnable r) {
 466                    return new Thread(r, "Pipe writer thread: " + name);
 467                }
 468            });
 469        }
 470        return new SynchronousExecutorService();
 471    }
 472
 473    /**
 474     * Sends a command to the remote end and executes it there.
 475     * <p/>
 476     * <p/>
 477     * This is the lowest layer of abstraction in {@link Channel}.
 478     * {@link Command}s are executed on a remote system in the order they are sent.
 479     */
 480    /*package*/
 481    synchronized void send(Command cmd) throws IOException {
 482        if (outClosed != null) {
 483            throw new ChannelClosedException(outClosed);
 484        }
 485        if (logger.isLoggable(Level.FINE)) {
 486            logger.fine("Send " + cmd);
 487        }
 488        Channel old = Channel.setCurrent(this);
 489        try {
 490            oos.writeObject(cmd);
 491            oos.flush();        // make sure the command reaches the other end.
 492        } finally {
 493            Channel.setCurrent(old);
 494        }
 495        // unless this is the last command, have OOS and remote OIS forget all the objects we sent
 496        // in this command. Otherwise it'll keep objects in memory unnecessarily.
 497        // However, this may fail if the command was the close, because that's supposed to be the last command
 498        // ever sent. See the comment from jglick on HUDSON-3077 about what happens if we do oos.reset(). 
 499        if (!(cmd instanceof CloseCommand)) {
 500            oos.reset();
 501        }
 502    }
 503
 504    /**
 505     * {@inheritDoc}
 506     */
 507    public <T> T export(Class<T> type, T instance) {
 508        return export(type, instance, true);
 509    }
 510
 511    /**
 512     * @param userProxy If true, the returned proxy will be capable to handle classes
 513     * defined in the user classloader as parameters and return values.
 514     * Such proxy relies on {@link RemoteClassLoader} and related mechanism,
 515     * so it's not usable for implementing lower-layer services that are
 516     * used by {@link RemoteClassLoader}.
 517     * <p/>
 518     * To create proxies for objects inside remoting, pass in false.
 519     */
 520    /*package*/ <T> T export(Class<T> type, T instance, boolean userProxy) {
 521        if (instance == null) {
 522            return null;
 523        }
 524
 525        // every so often perform GC on the remote system so that
 526        // unused RemoteInvocationHandler get released, which triggers
 527        // unexport operation.
 528        if ((++gcCounter) % 10000 == 0) {
 529            try {
 530                send(new GCCommand());
 531            } catch (IOException e) {
 532                // for compatibility reason we can't change the export method signature
 533                logger.log(Level.WARNING, "Unable to send GC command", e);
 534            }
 535        }
 536
 537        // proxy will unexport this instance when it's GC-ed on the remote machine.
 538        final int id = export(instance);
 539        return RemoteInvocationHandler.wrap(null, id, type, userProxy, exportedObjects.isRecording());
 540    }
 541
 542    /*package*/ int export(Object instance) {
 543        return exportedObjects.export(instance);
 544    }
 545
 546    /*package*/ int export(Object instance, boolean automaticUnexport) {
 547        return exportedObjects.export(instance, automaticUnexport);
 548    }
 549
 550    /*package*/ Object getExportedObject(int oid) {
 551        return exportedObjects.get(oid);
 552    }
 553
 554    /*package*/ void unexport(int id) {
 555        exportedObjects.unexportByOid(id);
 556    }
 557
 558    /**
 559     * Preloads jar files on the remote side.
 560     * <p/>
 561     * <p/>
 562     * This is a performance improvement method that can be safely
 563     * ignored if your goal is just to make things working.
 564     * <p/>
 565     * <p/>
 566     * Normally, classes are transferred over the network one at a time,
 567     * on-demand. This design is mainly driven by how Java classloading works
 568     * &mdash; we can't predict what classes will be necessarily upfront very easily.
 569     * <p/>
 570     * <p/>
 571     * Classes are loaded only once, so for long-running {@link Channel},
 572     * this is normally an acceptable overhead. But sometimes, for example
 573     * when a channel is short-lived, or when you know that you'll need
 574     * a majority of classes in certain jar files, then it is more efficient
 575     * to send a whole jar file over the network upfront and thereby
 576     * avoiding individual class transfer over the network.
 577     * <p/>
 578     * <p/>
 579     * That is what this method does. It ensures that a series of jar files
 580     * are copied to the remote side (AKA "preloading.")
 581     * Classloading will consult the preloaded jars before performing
 582     * network transfer of class files.
 583     *
 584     * @param classLoaderRef This parameter is used to identify the remote classloader
 585     * that will prefetch the specified jar files. That is, prefetching
 586     * will ensure that prefetched jars will kick in
 587     * when this {@link Callable} object is actually executed remote side.
 588     * <p/>
 589     * <p/>
 590     * {@link RemoteClassLoader}s are created wisely, one per local {@link ClassLoader},
 591     * so this parameter doesn't have to be exactly the same {@link Callable}
 592     * to be executed later &mdash; it just has to be of the same class.
 593     * @param classesInJar {@link Class} objects that identify jar files to be preloaded.
 594     * Jar files that contain the specified classes will be preloaded into the remote peer.
 595     * You just need to specify one class per one jar.
 596     * @return true if the preloading actually happened. false if all the jars
 597     *         are already preloaded. This method is implemented in such a way that
 598     *         unnecessary jar file transfer will be avoided, and the return value
 599     *         will tell you if this optimization kicked in. Under normal circumstances
 600     *         your program shouldn't depend on this return value. It's just a hint.
 601     * @throws IOException if the preloading fails.
 602     */
 603    public boolean preloadJar(Callable<?, ?> classLoaderRef, Class... classesInJar)
 604        throws IOException, InterruptedException {
 605        return preloadJar(UserRequest.getClassLoader(classLoaderRef), classesInJar);
 606    }
 607
 608    public boolean preloadJar(ClassLoader local, Class... classesInJar) throws IOException, InterruptedException {
 609        URL[] jars = new URL[classesInJar.length];
 610        for (int i = 0; i < classesInJar.length; i++) {
 611            jars[i] = Which.jarFile(classesInJar[i]).toURI().toURL();
 612        }
 613        return call(new PreloadJarTask(jars, local));
 614    }
 615
 616    public boolean preloadJar(ClassLoader local, URL... jars) throws IOException, InterruptedException {
 617        return call(new PreloadJarTask(jars, local));
 618    }
 619
 620    PipeWindow getPipeWindow(int oid) {
 621        synchronized (pipeWindows) {
 622            Key k = new Key(oid);
 623            WeakReference<PipeWindow> v = pipeWindows.get(k);
 624            if (v != null) {
 625                PipeWindow w = v.get();
 626                if (w != null) {
 627                    return w;
 628                }
 629            }
 630            PipeWindow w;
 631            if (remoteCapability.supportsPipeThrottling()) {
 632                w = new Real(k, PIPE_WINDOW_SIZE);
 633            } else {
 634                w = new PipeWindow.Fake();
 635            }
 636            pipeWindows.put(k, new WeakReference<PipeWindow>(w));
 637            return w;
 638        }
 639    }
 640
 641
 642    /**
 643     * {@inheritDoc}
 644     */
 645    public <V, T extends Throwable>
 646    V call(Callable<V, T> callable) throws IOException, T, InterruptedException {
 647        UserRequest<V, T> request = null;
 648        try {
 649            request = new UserRequest<V, T>(this, callable);
 650            UserResponse<V, T> r = request.call(this);
 651            return r.retrieve(this, UserRequest.getClassLoader(callable));
 652
 653            // re-wrap the exception so that we can capture the stack trace of the caller.
 654        } catch (ClassNotFoundException e) {
 655            IOException x = new IOException("Remote call on " + name + " failed");
 656            x.initCause(e);
 657            throw x;
 658        } catch (Error e) {
 659            IOException x = new IOException("Remote call on " + name + " failed");
 660            x.initCause(e);
 661            throw x;
 662        } finally {
 663            // since this is synchronous operation, when the round trip is over
 664            // we assume all the exported objects are out of scope.
 665            // (that is, the operation shouldn't spawn a new thread or altter
 666            // global state in the remote system.
 667            if (request != null) {
 668                request.releaseExports();
 669            }
 670        }
 671    }
 672
 673    /**
 674     * {@inheritDoc}
 675     */
 676    public <V, T extends Throwable>
 677    Future<V> callAsync(final Callable<V, T> callable) throws IOException {
 678        final Future<UserResponse<V, T>> f = new UserRequest<V, T>(this, callable).callAsync(this);
 679        return new FutureAdapter<V, UserResponse<V, T>>(f) {
 680            protected V adapt(UserResponse<V, T> r) throws ExecutionException {
 681                try {
 682                    return r.retrieve(Channel.this, UserRequest.getClassLoader(callable));
 683                } catch (Throwable t) {// really means catch(T t)
 684                    throw new ExecutionException(t);
 685                }
 686            }
 687        };
 688    }
 689
 690    /*
 691     * This method provides a mean to flush the I/O pipe associated with this
 692     * channel. Useful when the process associated with the channel is terminating
 693     * but the pipe might still transmitting data.
 694     * See http://issues.hudson-ci.org/browse/HUDSON-7809
 695     */
 696    public void flushPipe() throws IOException, InterruptedException {
 697        // The solution is to create no-op dummy RemotePipeWriter  callable and submit
 698        // to the channel synchronously.
 699
 700
 701        try {
 702            pipeWriter.submit(new Runnable() {
 703
 704                public void run() {
 705                    // Do nothing, just a dummy runnable just to flush
 706                    // this side of the Pipe
 707                }
 708            }).get();
 709        } catch (ExecutionException exc) {
 710            throw new AssertionError(exc);
 711        }
 712        // Do not use anonymous class, other wise whole class gets marshalled over pipe and
 713        // the channel class is not serializable.
 714        call(new DummyRemotePipeWriterCallable());
 715    }
 716
 717    public static class DummyRemotePipeWriterCallable implements Callable<Object, InterruptedException>, Serializable {
 718
 719        public Object call() throws InterruptedException {
 720            try {
 721                return Channel.current().pipeWriter.submit(new Runnable() {
 722
 723                    public void run() {
 724                        // Do nothing, just a dummy runnable just to flush
 725                        // other side of the Pipe
 726                    }
 727                }).get();
 728            } catch (ExecutionException exc) {
 729                throw new AssertionError(exc);
 730            }
 731        }
 732    }
 733
 734    ;
 735
 736
 737    /**
 738     * Aborts the connection in response to an error.
 739     *
 740     * @param e The error that caused the connection to be aborted. Never null.
 741     */
 742    protected synchronized void terminate(IOException e) {
 743        if (e == null) {
 744            throw new IllegalArgumentException();
 745        }
 746        outClosed = inClosed = e;
 747        try {
 748            synchronized (pendingCalls) {
 749                for (Request<?, ?> req : pendingCalls.values()) {
 750                    req.abort(e);
 751                }
 752                pendingCalls.clear();
 753            }
 754            synchronized (executingCalls) {
 755                for (Request<?, ?> r : executingCalls.values()) {
 756                    java.util.concurrent.Future<?> f = r.future;
 757                    if (f != null) {
 758                        f.cancel(true);
 759                    }
 760                }
 761                executingCalls.clear();
 762            }
 763        } finally {
 764            notifyAll();
 765
 766            if (e instanceof OrderlyShutdown) {
 767                e = null;
 768            }
 769            for (Listener l : listeners.toArray(new Listener[listeners.size()])) {
 770                l.onClosed(this, e);
 771            }
 772        }
 773    }
 774
 775    /**
 776     * Registers a new {@link Listener}.
 777     *
 778     * @see #removeListener(Listener)
 779     */
 780    public void addListener(Listener l) {
 781        listeners.add(l);
 782    }
 783
 784    /**
 785     * Removes a listener.
 786     *
 787     * @return false if the given listener has not been registered to begin with.
 788     */
 789    public boolean removeListener(Listener l) {
 790        return listeners.remove(l);
 791    }
 792
 793    /**
 794     * Waits for this {@link Channel} to be closed down.
 795     * <p/>
 796     * The close-down of a {@link Channel} might be initiated locally or remotely.
 797     *
 798     * @throws InterruptedException If the current thread is interrupted while waiting for the completion.
 799     */
 800    public synchronized void join() throws InterruptedException {
 801        while (inClosed == null || outClosed == null) {
 802            wait();
 803        }
 804    }
 805
 806    /**
 807     * If the receiving end of the channel is closed (that is, if we are guaranteed to receive nothing further),
 808     * this method returns true.
 809     */
 810    /*package*/ boolean isInClosed() {
 811        return inClosed != null;
 812    }
 813
 814    /**
 815     * Waits for this {@link Channel} to be closed down, but only up the given milliseconds.
 816     *
 817     * @throws InterruptedException If the current thread is interrupted while waiting for the completion.
 818     * @since 1.299
 819     */
 820    public synchronized void join(long timeout) throws InterruptedException {
 821        long start = System.currentTimeMillis();
 822        while (System.currentTimeMillis() - start < timeout && (inClosed == null || outClosed == null)) {
 823            wait(timeout + start - System.currentTimeMillis());
 824        }
 825    }
 826
 827    /**
 828     * Notifies the remote peer that we are closing down.
 829     * <p/>
 830     * Execution of this command also triggers the {@link ReaderThread} to shut down
 831     * and quit. The {@link CloseCommand} is always the last command to be sent on
 832     * {@link ObjectOutputStream}, and it's the last command to be read.
 833     */
 834    private static final class CloseCommand extends Command {
 835        protected void execute(Channel channel) {
 836            try {
 837                channel.close();
 838                channel.terminate(new OrderlyShutdown(createdAt));
 839            } catch (IOException e) {
 840                logger.log(Level.SEVERE, "close command failed on " + channel.name, e);
 841                logger.log(Level.INFO, "close command created at", createdAt);
 842            }
 843        }
 844
 845        @Override
 846        public String toString() {
 847            return "close";
 848        }
 849    }
 850
 851    /**
 852     * Signals the orderly shutdown of the channel, but captures
 853     * where the termination was initiated as a nested exception.
 854     */
 855    private static final class OrderlyShutdown extends IOException {
 856        private OrderlyShutdown(Throwable cause) {
 857            super(cause.getMessage());
 858            initCause(cause);
 859        }
 860
 861        private static final long serialVersionUID = 1L;
 862    }
 863
 864    /**
 865     * Resets all the performance counters.
 866     */
 867    public void resetPerformanceCounters() {
 868        classLoadingCount.set(0);
 869        classLoadingTime.set(0);
 870        resourceLoadingCount.set(0);
 871        resourceLoadingTime.set(0);
 872    }
 873
 874    /**
 875     * {@inheritDoc}
 876     */
 877    public synchronized void close() throws IOException {
 878        if (outClosed != null) {
 879            return;  // already closed
 880        }
 881
 882        send(new CloseCommand());
 883        outClosed
 884            = new IOException();   // last command sent. no further command allowed. lock guarantees that no command will slip inbetween
 885        try {
 886            oos.close();
 887        } catch (IOException e) {
 888            // there's a race condition here.
 889            // the remote peer might have already responded to the close command
 890            // and closed the connection, in which case our close invocation
 891            // could fail with errors like
 892            // "java.io.IOException: The pipe is being closed"
 893            // so let's ignore this error.
 894        }
 895
 896        // termination is done by CloseCommand when we received it.
 897    }
 898
 899    /**
 900     * Gets the application specific property set by {@link #setProperty(Object, Object)}.
 901     * These properties are also accessible from the remote channel via {@link #getRemoteProperty(Object)}.
 902     * <p/>
 903     * <p/>
 904     * This mechanism can be used for one side to discover contextual objects created by the other JVM
 905     * (as opposed to executing {@link Callable}, which cannot have any reference to the context
 906     * of the remote {@link Channel}.
 907     */
 908    public Object getProperty(Object key) {
 909        return properties.get(key);
 910    }
 911
 912    public <T> T getProperty(ChannelProperty<T> key) {
 913        return key.type.cast(properties.get(key));
 914    }
 915
 916    /**
 917     * Works like {@link #getProperty(Object)} but wait until some value is set by someone.
 918     */
 919    public Object waitForProperty(Object key) throws InterruptedException {
 920        synchronized (properties) {
 921            while (true) {
 922                Object v = properties.get(key);
 923                if (v != null) {
 924                    return v;
 925                }
 926                properties.wait();
 927            }
 928        }
 929    }
 930
 931    /**
 932     * Sets the property value on this side of the channel.
 933     *
 934     * @see #getProperty(Object)
 935     */
 936    public Object setProperty(Object key, Object value) {
 937        synchronized (properties) {
 938            Object old = value != null ? properties.put(key, value) : properties.remove(key);
 939            properties.notifyAll();
 940            return old;
 941        }
 942    }
 943
 944    public Object getRemoteProperty(Object key) {
 945        return remoteChannel.getProperty(key);
 946    }
 947
 948    public Object waitForRemoteProperty(Object key) throws InterruptedException {
 949        return remoteChannel.waitForProperty(key);
 950    }
 951
 952    /**
 953     * Starts a local to remote port forwarding (the equivalent of "ssh -L").
 954     *
 955     * @param recvPort The port on this local machine that we'll listen to. 0 to let
 956     * OS pick a random available port. If you specify 0, use
 957     * {@link ListeningPort#getPort()} to figure out the actual assigned port.
 958     * @param forwardHost The remote host that the connection will be forwarded to.
 959     * Connection to this host will be made from the other JVM that
 960     * this {@link Channel} represents.
 961     * @param forwardPort The remote port that the connection will be forwarded to.
 962     * @return
 963     */
 964    public ListeningPort createLocalToRemotePortForwarding(int recvPort, String forwardHost, int forwardPort)
 965        throws IOException, InterruptedException {
 966        return new PortForwarder(recvPort,
 967            ForwarderFactory.create(this, forwardHost, forwardPort));
 968    }
 969
 970    /**
 971     * Starts a remote to local port forwarding (the equivalent of "ssh -R").
 972     *
 973     * @param recvPort The port on the remote JVM (represented by this {@link Channel})
 974     * that we'll listen to. 0 to let
 975     * OS pick a random available port. If you specify 0, use
 976     * {@link ListeningPort#getPort()} to figure out the actual assigned port.
 977     * @param forwardHost The remote host that the connection will be forwarded to.
 978     * Connection to this host will be made from this JVM.
 979     * @param forwardPort The remote port that the connection will be forwarded to.
 980     * @return
 981     */
 982    public ListeningPort createRemoteToLocalPortForwarding(int recvPort, String forwardHost, int forwardPort)
 983        throws IOException, InterruptedException {
 984        return PortForwarder.create(this, recvPort,
 985            ForwarderFactory.create(forwardHost, forwardPort));
 986    }
 987
 988    @Override
 989    public String toString() {
 990        return super.toString() + ":" + name;
 991    }
 992
 993    /**
 994     * Dumps the list of exported objects and their allocation traces to the given output.
 995     */
 996    public void dumpExportTable(PrintWriter w) throws IOException {
 997        exportedObjects.dump(w);
 998    }
 999
1000    public ExportList startExportRecording() {
1001        return exportedObjects.startRecording();
1002    }
1003
1004    /**
1005     * @see #lastHeard
1006     */
1007    public long getLastHeard() {
1008        return lastHeard;
1009    }
1010
1011    private final class ReaderThread extends Thread {
1012        public ReaderThread(String name) {
1013            super("Channel reader thread: " + name);
1014        }
1015
1016        @Override
1017        public void run() {
1018            Command cmd = null;
1019            try {
1020                while (inClosed == null) {
1021                    try {
1022                        Channel old = Channel.setCurrent(Channel.this);
1023                        try {
1024                            cmd = (Command) ois.readObject();
1025                            lastHeard = System.currentTimeMillis();
1026                        } finally {
1027                            Channel.setCurrent(old);
1028                        }
1029                    } catch (EOFException e) {
1030                        IOException ioe = new IOException("Unexpected termination of the channel");
1031                        ioe.initCause(e);
1032                        throw ioe;
1033                    } catch (ClassNotFoundException e) {
1034                        logger.log(Level.SEVERE, "Unable to read a command (channel " + name + ")", e);
1035                    }
1036                    if (logger.isLoggable(Level.FINE)) {
1037                        logger.fine("Received " + cmd);
1038                    }
1039                    try {
1040                        cmd.execute(Channel.this);
1041                    } catch (Throwable t) {
1042                        logger.log(Level.SEVERE, "Failed to execute command " + cmd + " (channel " + name + ")", t);
1043                        logger.log(Level.SEVERE, "This command is created here", cmd.createdAt);
1044                    }
1045                }
1046                ois.close();
1047            } catch (IOException e) {
1048                logger.log(Level.SEVERE, "I/O error in channel " + name, e);
1049                terminate(e);
1050            } finally {
1051                pipeWriter.shutdown();
1052            }
1053        }
1054    }
1055
1056    /*package*/
1057    static Channel setCurrent(Channel channel) {
1058        Channel old = CURRENT.get();
1059        CURRENT.set(channel);
1060        return old;
1061    }
1062
1063    /**
1064     * This method can be invoked during the serialization/deserialization of
1065     * objects when they are transferred to the remote {@link Channel},
1066     * as well as during {@link Callable#call()} is invoked.
1067     *
1068     * @return null
1069     *         if the calling thread is not performing serialization.
1070     */
1071    public static Channel current() {
1072        return CURRENT.get();
1073    }
1074
1075    /**
1076     * Remembers the current "channel" associated for this thread.
1077     */
1078    private static final ThreadLocal<Channel> CURRENT = new ThreadLocal<Channel>();
1079
1080    private static final Logger logger = Logger.getLogger(Channel.class.getName());
1081
1082    public static final int PIPE_WINDOW_SIZE = Integer.getInteger(Channel.class + ".pipeWindowSize", 128 * 1024);
1083
1084//    static {
1085//        ConsoleHandler h = new ConsoleHandler();
1086//        h.setFormatter(new Formatter(){
1087//            public synchronized String format(LogRecord record) {
1088//                StringBuilder sb = new StringBuilder();
1089//                sb.append((record.getMillis()%100000)+100000);
1090//                sb.append(" ");
1091//                if (record.getSourceClassName() != null) {
1092//                    sb.append(record.getSourceClassName());
1093//                } else {
1094//                    sb.append(record.getLoggerName());
1095//                }
1096//                if (record.getSourceMethodName() != null) {
1097//                    sb.append(" ");
1098//                    sb.append(record.getSourceMethodName());
1099//                }
1100//                sb.append('\n');
1101//                String message = formatMessage(record);
1102//                sb.append(record.getLevel().getLocalizedName());
1103//                sb.append(": ");
1104//                sb.append(message);
1105//                sb.append('\n');
1106//                if (record.getThrown() != null) {
1107//                    try {
1108//                        StringWriter sw = new StringWriter();
1109//                        PrintWriter pw = new PrintWriter(sw);
1110//                        record.getThrown().printStackTrace(pw);
1111//                        pw.close();
1112//                        sb.append(sw.toString());
1113//                    } catch (Exception ex) {
1114//                    }
1115//                }
1116//                return sb.toString();
1117//            }
1118//        });
1119//        h.setLevel(Level.FINE);
1120//        logger.addHandler(h);
1121//        logger.setLevel(Level.FINE);
1122//    }
1123}