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