/** Initiate a new multiplexed connection through the underlying connection. */
  public synchronized TCPConnection openConnection() throws IOException {
    // generate ID that should not be already used
    // If all possible 32768 IDs are used,
    // this method will block searching for a new ID forever.
    int id;
    do {
      lastID = (++lastID) & 0x7FFF;
      id = lastID;

      // The orig flag (copied to the high bit of the ID) is used
      // to have two distinct ranges to choose IDs from for the
      // two endpoints.
      if (orig) id |= 0x8000;
    } while (connectionTable.get(id) != null);

    // create multiplexing streams and bookkeeping information
    MultiplexConnectionInfo info = new MultiplexConnectionInfo(id);
    info.in = new MultiplexInputStream(this, info, 2048);
    info.out = new MultiplexOutputStream(this, info, 2048);

    // add to connection table if multiplexer has not died
    synchronized (connectionTable) {
      if (!alive) throw new IOException("Multiplexer connection dead");
      if (numConnections >= maxConnections)
        throw new IOException(
            "Cannot exceed " + maxConnections + " simultaneous multiplexed connections");
      connectionTable.put(id, info);
      ++numConnections;
    }

    // inform remote endpoint of new connection
    synchronized (dataOut) {
      try {
        dataOut.writeByte(OPEN);
        dataOut.writeShort(id);
        dataOut.flush();
      } catch (IOException e) {
        multiplexLog.log(Log.BRIEF, "exception: ", e);

        shutDown();
        throw e;
      }
    }

    return new TCPConnection(channel, info.in, info.out);
  }
  /**
   * Acknowledge remote endpoint's closing of connection.
   *
   * @param info connection information structure
   */
  void sendCloseAck(MultiplexConnectionInfo info) throws IOException {
    synchronized (dataOut) {
      if (alive && !info.closed)
        try {
          dataOut.writeByte(CLOSEACK);
          dataOut.writeShort(info.id);
          dataOut.flush();
          info.closed = true;
        } catch (IOException e) {
          multiplexLog.log(Log.BRIEF, "exception: ", e);

          shutDown();
          throw e;
        }
    }
  }
  /** Process multiplexing protocol received from underlying connection. */
  public void run() throws IOException {
    try {
      int op, id, length;
      MultiplexConnectionInfo info;

      while (true) {

        // read next op code from remote endpoint
        op = dataIn.readUnsignedByte();
        switch (op) {

            // remote endpoint initiating new connection
          case OPEN:
            id = dataIn.readUnsignedShort();

            if (multiplexLog.isLoggable(Log.VERBOSE)) {
              multiplexLog.log(Log.VERBOSE, "operation  OPEN " + id);
            }

            info = connectionTable.get(id);
            if (info != null) throw new IOException("OPEN: Connection ID already exists");
            info = new MultiplexConnectionInfo(id);
            info.in = new MultiplexInputStream(this, info, 2048);
            info.out = new MultiplexOutputStream(this, info, 2048);
            synchronized (connectionTable) {
              connectionTable.put(id, info);
              ++numConnections;
            }
            sun.rmi.transport.Connection conn;
            conn = new TCPConnection(channel, info.in, info.out);
            channel.acceptMultiplexConnection(conn);
            break;

            // remote endpoint closing connection
          case CLOSE:
            id = dataIn.readUnsignedShort();

            if (multiplexLog.isLoggable(Log.VERBOSE)) {
              multiplexLog.log(Log.VERBOSE, "operation  CLOSE " + id);
            }

            info = connectionTable.get(id);
            if (info == null) throw new IOException("CLOSE: Invalid connection ID");
            info.in.disconnect();
            info.out.disconnect();
            if (!info.closed) sendCloseAck(info);
            synchronized (connectionTable) {
              connectionTable.remove(id);
              --numConnections;
            }
            break;

            // remote endpoint acknowledging close of connection
          case CLOSEACK:
            id = dataIn.readUnsignedShort();

            if (multiplexLog.isLoggable(Log.VERBOSE)) {
              multiplexLog.log(Log.VERBOSE, "operation  CLOSEACK " + id);
            }

            info = connectionTable.get(id);
            if (info == null) throw new IOException("CLOSEACK: Invalid connection ID");
            if (!info.closed) throw new IOException("CLOSEACK: Connection not closed");
            info.in.disconnect();
            info.out.disconnect();
            synchronized (connectionTable) {
              connectionTable.remove(id);
              --numConnections;
            }
            break;

            // remote endpoint declaring additional bytes receivable
          case REQUEST:
            id = dataIn.readUnsignedShort();
            info = connectionTable.get(id);
            if (info == null) throw new IOException("REQUEST: Invalid connection ID");
            length = dataIn.readInt();

            if (multiplexLog.isLoggable(Log.VERBOSE)) {
              multiplexLog.log(Log.VERBOSE, "operation  REQUEST " + id + ": " + length);
            }

            info.out.request(length);
            break;

            // remote endpoint transmitting data packet
          case TRANSMIT:
            id = dataIn.readUnsignedShort();
            info = connectionTable.get(id);
            if (info == null) throw new IOException("SEND: Invalid connection ID");
            length = dataIn.readInt();

            if (multiplexLog.isLoggable(Log.VERBOSE)) {
              multiplexLog.log(Log.VERBOSE, "operation  TRANSMIT " + id + ": " + length);
            }

            info.in.receive(length, dataIn);
            break;

          default:
            throw new IOException("Invalid operation: " + Integer.toHexString(op));
        }
      }
    } finally {
      shutDown();
    }
  }