Logo Search packages:      
Sourcecode: jenkins-trilead-ssh2 version File versions  Download package

Channel.java

package com.trilead.ssh2.channel;

/**
 * Channel.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
00010 public class Channel
{
      /*
       * OK. Here is an important part of the JVM Specification:
       * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
       * 
       * Any association between locks and variables is purely conventional.
       * Locking any lock conceptually flushes all variables from a thread's
       * working memory, and unlocking any lock forces the writing out to main
       * memory of all variables that the thread has assigned. That a lock may be
       * associated with a particular object or a class is purely a convention.
       * (...)
       * 
       * If a thread uses a particular shared variable only after locking a
       * particular lock and before the corresponding unlocking of that same lock,
       * then the thread will read the shared value of that variable from main
       * memory after the lock operation, if necessary, and will copy back to main
       * memory the value most recently assigned to that variable before the
       * unlock operation.
       * 
       * This, in conjunction with the mutual exclusion rules for locks, suffices
       * to guarantee that values are correctly transmitted from one thread to
       * another through shared variables.
       * 
       * ====> Always keep that in mind when modifying the Channel/ChannelManger
       * code.
       * 
       */

      static final int STATE_OPENING = 1;
      static final int STATE_OPEN = 2;
      static final int STATE_CLOSED = 4;

      static final int CHANNEL_BUFFER_SIZE = 30000;

      /*
       * To achieve correctness, the following rules have to be respected when
       * accessing this object:
       */

      // These fields can always be read
      final ChannelManager cm;
      final ChannelOutputStream stdinStream;
      final ChannelInputStream stdoutStream;
      final ChannelInputStream stderrStream;

      // These two fields will only be written while the Channel is in state
      // STATE_OPENING.
      // The code makes sure that the two fields are written out when the state is
      // changing to STATE_OPEN.
      // Therefore, if you know that the Channel is in state STATE_OPEN, then you
      // can read these two fields without synchronizing on the Channel. However, make
      // sure that you get the latest values (e.g., flush caches by synchronizing on any
      // object). However, to be on the safe side, you can lock the channel.

      int localID = -1;
      int remoteID = -1;

      /*
       * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
       * msg.
       * 
       * This is a little bit complicated, but we have to do it in that way, since
       * we cannot keep a lock on the Channel during the send operation (this
       * would block sometimes the receiver thread, and, in extreme cases, can
       * lead to a deadlock on both sides of the connection (senders are blocked
       * since the receive buffers on the other side are full, and receiver
       * threads wait for the senders to finish). It all depends on the
       * implementation on the other side. But we cannot make any assumptions, we
       * have to assume the worst case. Confused? Just believe me.
       */

      /*
       * If you send a message on a channel, then you have to aquire the
       * "channelSendLock" and check the "closeMessageSent" flag (this variable
       * may only be accessed while holding the "channelSendLock" !!!
       * 
       * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
       * above.
       */

      final Object channelSendLock = new Object();
      boolean closeMessageSent = false;

      /*
       * Stop memory fragmentation by allocating this often used buffer.
       * May only be used while holding the channelSendLock
       */

      final byte[] msgWindowAdjust = new byte[9];

      // If you access (read or write) any of the following fields, then you have
      // to synchronize on the channel.

      int state = STATE_OPENING;

      boolean closeMessageRecv = false;

      /* This is a stupid implementation. At the moment we can only wait
       * for one pending request per channel.
       */
      int successCounter = 0;
      int failedCounter = 0;

      int localWindow = 0; /* locally, we use a small window, < 2^31 */
      long remoteWindow = 0; /* long for readable  2^32 - 1 window support */

      int localMaxPacketSize = -1;
      int remoteMaxPacketSize = -1;

      final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE];
      final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE];

      int stdoutReadpos = 0;
      int stdoutWritepos = 0;
      int stderrReadpos = 0;
      int stderrWritepos = 0;

      boolean EOF = false;

      Integer exit_status;

      String exit_signal;

      // we keep the x11 cookie so that this channel can be closed when this
      // specific x11 forwarding gets stopped

      String hexX11FakeCookie;

      // reasonClosed is special, since we sometimes need to access it
      // while holding the channelSendLock.
      // We protect it with a private short term lock.

      private final Object reasonClosedLock = new Object();
      private String reasonClosed = null;

      public Channel(ChannelManager cm)
      {
            this.cm = cm;

            this.localWindow = CHANNEL_BUFFER_SIZE;
            this.localMaxPacketSize = 35000 - 1024; // leave enough slack

            this.stdinStream = new ChannelOutputStream(this);
            this.stdoutStream = new ChannelInputStream(this, false);
            this.stderrStream = new ChannelInputStream(this, true);
      }

      /* Methods to allow access from classes outside of this package */

      public ChannelInputStream getStderrStream()
      {
            return stderrStream;
      }

      public ChannelOutputStream getStdinStream()
      {
            return stdinStream;
      }

      public ChannelInputStream getStdoutStream()
      {
            return stdoutStream;
      }

      public String getExitSignal()
      {
            synchronized (this)
            {
                  return exit_signal;
            }
      }

      public Integer getExitStatus()
      {
            synchronized (this)
            {
                  return exit_status;
            }
      }

      public String getReasonClosed()
      {
            synchronized (reasonClosedLock)
            {
                  return reasonClosed;
            }
      }

      public void setReasonClosed(String reasonClosed)
      {
            synchronized (reasonClosedLock)
            {
                  if (this.reasonClosed == null)
                        this.reasonClosed = reasonClosed;
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index