Package com.sun.jini.jeri.internal.mux

Source Code of com.sun.jini.jeri.internal.mux.Session$MuxInputStream

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.sun.jini.jeri.internal.mux;

import com.sun.jini.thread.Executor;
import com.sun.jini.thread.GetThreadPoolAction;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.jini.core.constraint.InvocationConstraints;
import net.jini.io.UnsupportedConstraintException;
import net.jini.io.context.AcknowledgmentSource;
import net.jini.jeri.InboundRequest;
import net.jini.jeri.OutboundRequest;

/**
* A Session represents a single session of a multiplexed connection,
* for either client-side and server-side perspective.  The particular
* role (CLIENT or SERVER) is indicated at construction time.
*
* @author Sun Microsystems, Inc.
**/
final class Session {

    static final int CLIENT = 0;
    static final int SERVER = 1;

    private static final int IDLE  = 0;
    private static final int OPEN  = 1;
    private static final int FINISHED  = 2;
    private static final int TERMINATED  = 3;
    private static final String[] stateNames = {
  "idle", "open", "finished", "terminated"
    };

    /**
     * pool of threads for executing tasks in system thread group: used for
     * I/O (reader and writer) threads and other asynchronous tasks
     **/
    private static final Executor systemThreadPool =
  (Executor) AccessController.doPrivileged(
      new GetThreadPoolAction(false));

    /** mux logger */
    private static final Logger logger =
  Logger.getLogger("net.jini.jeri.connection.mux");

    private final Mux mux;
    private final int sessionID;
    private final int role;

    private final OutputStream out;
    private final InputStream in;

    /** lock guarding all mutable instance state (below) */
    private final Object sessionLock = new Object();

    private boolean sessionDown = false;
    private String sessionDownMessage;
    private Throwable sessionDownCause;

    private int outState;
    private int outRation;
    private final boolean outRationInfinite;
    private boolean partialDeliveryStatus = false;

    private int inState;
    private int inRation;
    private final boolean inRationInfinite;
    private int inBufRemaining = 0;
    private final LinkedList inBufQueue = new LinkedList();
    private int inBufPos = 0;
    private boolean inEOF = false;
    private boolean inClosed = false;

    private boolean fakeOKtoWrite = false;    // REMIND
    private boolean removeLater = false;    // REMIND

    private boolean receivedAckRequired = false;
    private boolean sentAcknowledgment = false;

    private Collection ackListeners = null;
    private boolean sentAckRequired = false;
    private boolean receivedAcknowledgment = false;

    /**
     *
     */
    Session(Mux mux, int sessionID, int role) {
  this.mux = mux;
  this.sessionID = sessionID;
  this.role = role;

  out = new MuxOutputStream();
  in = new MuxInputStream();

  outState = (role == CLIENT ? IDLE : OPEN);
  outRation = mux.initialOutboundRation;
  outRationInfinite = (outRation == 0);

  inState = (role == CLIENT ? IDLE : OPEN);
  inRation = mux.initialInboundRation;
  inRationInfinite = (inRation == 0);
    }

    /**
     *
     */
    OutboundRequest getOutboundRequest() {
  assert role == CLIENT;
  return new OutboundRequest() {
      public void populateContext(Collection context) {
    ((MuxClient) mux).populateContext(context);
      }
      public InvocationConstraints getUnfulfilledConstraints() {
    /*
     * NYI: We currently have no request-specific hook
     * back to the transport implementation, so we must
     * depend on OutboundRequest wrapping for this method.
     */
    throw new AssertionError();
      }
      public OutputStream getRequestOutputStream() { return out; }
      public InputStream getResponseInputStream() { return in; }
      public boolean getDeliveryStatus() {
    synchronized (sessionLock) {
        return partialDeliveryStatus;
    }
      }
      public void abort() { Session.this.abort(); }
  };
    }

    /**
     *
     */
    InboundRequest getInboundRequest() {
  assert role == SERVER;
  return new InboundRequest() {
      public void checkPermissions() {
    ((MuxServer) mux).checkPermissions();
      }
      public InvocationConstraints
    checkConstraints(InvocationConstraints constraints)
    throws UnsupportedConstraintException
      {
    return ((MuxServer) mux).checkConstraints(constraints);
      }
      public void populateContext(Collection context) {
    context.add(new AcknowledgmentSource() {
        public boolean addAcknowledgmentListener(
      AcknowledgmentSource.Listener listener)
        {
      if (listener == null) {
          throw new NullPointerException();
      }
      synchronized (sessionLock) {
          if (outState < FINISHED) {
        if (ackListeners == null) {
            ackListeners = new ArrayList(3);
        }
        ackListeners.add(listener);
        return true;
          } else {
        return false;
          }
      }
        }
    });
    ((MuxServer) mux).populateContext(context);
      }
      public InputStream getRequestInputStream() { return in; }
      public OutputStream getResponseOutputStream() { return out; }
      public void abort() { Session.this.abort(); }
  };
    }

    /**
     *
     */
    void abort() {
  synchronized (sessionLock) {
      if (!sessionDown) {
    if (logger.isLoggable(Level.FINEST)) {
        logger.log(Level.FINEST,
      "outState=" + stateNames[outState] +
      ",inState=" + stateNames[inState] +
      ",role=" + (role == CLIENT ? "CLIENT" : "SERVER"));
    }

    if (outState == IDLE) {
        mux.removeSession(sessionID);
    } else if (outState < TERMINATED) {
        if (role == SERVER && outState == FINISHED) {
      /*
       * In this case, send Close rather than Abort, so that
       * a client that still hasn't finished writing will not
       * get an unnecessary failure and will be able to read
       * the complete response as intended (still permitting
       * server-side defensive abort() invocation).
       */
      mux.asyncSendClose(sessionID);
        } else {
      mux.asyncSendAbort(Mux.Abort | (role == SERVER ?
              Mux.Abort_partial : 0),
             sessionID, null);
        }
        setOutState(TERMINATED);
    }

    setDown("request aborted", null);
      }
      /*
       * After the application has invoked abort() on the request, we
       * must no longer try to "fake" an OK session.
       */
      fakeOKtoWrite = false;

      /*
       * If removing this session from the connection's table
       * was delayed in order to be able to send an
       * Acknowledgment message, then we remove it on local
       * abort in order to clean up resources.  Also make sure
       * that our state is considered terminated so that no
       * future Acknowledgment message will be sent.
       */
      if (removeLater) {
    if (outState < TERMINATED) {
        setOutState(TERMINATED);
    }
    mux.removeSession(sessionID);
    removeLater = false;
      }
  }
    }

    /**
     *
     */
    void setDown(String message, Throwable cause) {
  synchronized (sessionLock) {
      if (!sessionDown) {
    sessionDown = true;
    sessionDownMessage = message;
    sessionDownCause = cause;
    sessionLock.notifyAll();
      }
  }
    }

    /**
     *
     */
    void handleIncrementRation(int increment) throws ProtocolException {
  synchronized (sessionLock) {
      if (inState == IDLE || inState == TERMINATED) {
    throw new ProtocolException("IncrementRation on " +
        stateNames[inState] + " session: " + sessionID);
      }
      if (!outRationInfinite) {
    if (outRation + increment < outRation) {
        throw new ProtocolException("ration overflow");
    }
    if (outState == OPEN) {
        if (increment > 0) {
      if (outRation == 0) {
          sessionLock.notifyAll();
      }
      outRation += increment;
        }
    }
      } // ignore message if outbound ration is infinite
  }
    }

    /**
     *
     */
    void handleAbort(boolean partial) throws ProtocolException {
  synchronized (sessionLock) {
      if (inState == IDLE || inState == TERMINATED) {
    throw new ProtocolException("Abort on " +
        stateNames[inState] + " session: " + sessionID);
      }

      setInState(TERMINATED);
      partialDeliveryStatus = partial;

      /*
       * Respond with an abort of this side of the session, if it's
       * still open.
       */
      /*
       * REMIND: Technically, the client should not have to send
       * an Abort message here if it is already in the finished
       * state, although the spec would seem to suggest that it
       * should do so regardless.  A particular reason that we
       * send it here in that case, though, is that it should be
       * a cheap way to avoid 4827402 for that case-- to ensure
       * that no late Acknowledgment message gets sent after the
       * session has been removed.
       */
      if (outState < TERMINATED) {
    mux.asyncSendAbort(Mux.Abort | (role == SERVER ?
            Mux.Abort_partial : 0),
           sessionID, null);
    setOutState(TERMINATED);
      }

      setDown("request aborted by remote endpoint", null);

      if (sentAckRequired && !receivedAcknowledgment) {
    notifyAcknowledgmentListeners(false);
      // REMIND: what about other dangling acknowledgments?

      mux.removeSession(sessionID);
  }
    }

    /**
     *
     */
    void handleClose() throws ProtocolException {
  if (role != CLIENT) {
      throw new ProtocolException("Close sent by client");
  }

  synchronized (sessionLock) {
      if (inState != FINISHED) {
    throw new ProtocolException("Close on " +
        stateNames[inState] + " session: " + sessionID);
      }
      if (outState < FINISHED) {
    /*
     * From a protocol perspective, we need to terminate the
     * session at this point (because we're not finished, but
     * we don't want to hold on to it unnecessarily).  But we
     * also don't want the session to appear failed while the
     * client is still writing-- instead, we want the client
     * to be able to successfully read the complete response
     * that was received-- so this flag is set to
     * (temporarily) fake that the session is still in OK
     * shape (but not send any more data for it).
     */
    fakeOKtoWrite = true;
    mux.asyncSendAbort(Mux.Abort, sessionID, null);
    setOutState(TERMINATED);
    /*
     * REMIND: This approach causes a premature negative
     * acknowledgment to the server.  It seems that
     * ideally, if receivedAckRequired is true, we should
     * delay sending the Abort message until the response
     * input stream is closed and an Acknowledgment has
     * been sent-- although that would be somewhat at odds
     * with the "timely fashion" prescription of the Close
     * message specification.
     */
      }

      setInState(TERMINATED);

      setDown("request closed by server", null);

      /*
       * If we still (might) need to send an Acknowledgment,
       * then we must delay removing this session from the
       * connection's table now, to prevent the sessionID being
       * reused before the Acknowledgment message is sent.
       */
      if (outState == TERMINATED ||
    !receivedAckRequired || sentAcknowledgment)
      {
    mux.removeSession(sessionID);
      } else {
    removeLater = true;
      }
  }
    }

    /**
     *
     */
    void handleAcknowledgment() throws ProtocolException {
  if (role != SERVER) {
      throw new ProtocolException("Acknowledgment sent by server");
  }

  synchronized (sessionLock) {
      if (inState == IDLE || inState == TERMINATED) {
    throw new ProtocolException("Acknowledgment on " +
        stateNames[inState] + " session: " + sessionID);
      }
      if (outState < FINISHED) {
    throw new ProtocolException(
        "acknowledgment received before EOF sent");
      }
      if (!sentAckRequired) {
    throw new ProtocolException("acknowledgment not requested");
      }
      if (receivedAcknowledgment) {
    throw new ProtocolException("duplicate acknowledgment");
      }
      receivedAcknowledgment = true;

      notifyAcknowledgmentListeners(true);
  }
    }

    /**
     *
     */
    void handleData(ByteBuffer data,
        boolean eof, boolean close, boolean ackRequired)
  throws ProtocolException
    {
  assert eof || (!close && !ackRequired);

  if (ackRequired && role != CLIENT) {
      throw new ProtocolException("Data/ackRequired sent by client");
  }

  synchronized (sessionLock) {
      boolean notified = close;  // close always causes notification

      if (inState != OPEN) {
    throw new ProtocolException("Data on " +
        stateNames[inState] + " session: " + sessionID);
      }
      int length = data.remaining();
      if (!inRationInfinite && length > inRation) {
    throw new ProtocolException("input ration exceeded");
      }
      if (!inClosed && outState < TERMINATED) {
    if (length > 0) {
        if (inBufRemaining == 0) {
      sessionLock.notifyAll();
      notified = true;
        }
        inBufQueue.addLast(data);
        inBufRemaining += length;
        if (!inRationInfinite) {
      inRation -= length;
        }
    }
      }

      if (eof) {
    inEOF = true;
    setInState(FINISHED);
    if (!notified) {
        sessionLock.notifyAll();
    }

    if (ackRequired) {
        receivedAckRequired = true;
        // send acknowledgment if input stream already closed?
    }

    if (close) {
        handleClose();
    }
    // REMIND: send Close if appropriate?
      }
  }
    }

    /**
     *
     */
    void handleOpen() throws ProtocolException {
  assert role == SERVER;
  synchronized (sessionLock) {
      if (inState < FINISHED || outState < TERMINATED) {
    throw new ProtocolException(
                    inState < FINISHED ?
        ("Data/open on " +
         stateNames[inState] + " session: " + sessionID) :
        ("Data/open before previous session terminated"));
      }

      setInState(TERMINATED);
      // REMIND: process dangling acknowledgments here?

      setDown("old request", null)// extraneous?
      sessionLock.notifyAll();

      mux.removeSession(sessionID);
  }
    }

    /**
     *
     */
    private void setOutState(int newState) {
  assert newState > outState;
  outState = newState;
    }

    /**
     *
     */
    private void setInState(int newState) {
  assert newState > inState;
  inState = newState;
    }

    private void notifyAcknowledgmentListeners(final boolean received) {
  if (ackListeners != null) {
      systemThreadPool.execute(new Runnable() {
    public void run() {
        Iterator iter = ackListeners.iterator();
        while (iter.hasNext()) {
      AcknowledgmentSource.Listener listener =
          (AcknowledgmentSource.Listener) iter.next();
      listener.acknowledgmentReceived(received);
        }
    }
      }, "Mux ack notifier");
  }
    }

    /**
     * Output stream returned by OutboundRequests and InboundRequests for
     * a session of a multiplexed connection.
     */
    private class MuxOutputStream extends OutputStream {

  private ByteBuffer buffer = mux.directBuffersUseful() ?
      ByteBuffer.allocateDirect(mux.maxFragmentSize) :
      ByteBuffer.allocate(mux.maxFragmentSize);

  MuxOutputStream() { }

  public synchronized void write(int b) throws IOException {
      if (!buffer.hasRemaining()) {
    writeBuffer(false);
      } else {
    synchronized (sessionLock) {  // REMIND: necessary?
        ensureOpen();
    }
      }
      buffer.put((byte) b);
  }

  public synchronized void write(byte[] b, int off, int len)
      throws IOException
  {
      if (b == null) {
    throw new NullPointerException();
      } else if ((off < 0) || (off > b.length) || (len < 0) ||
           ((off + len) > b.length) || ((off + len) < 0))
      {
    throw new IndexOutOfBoundsException();
      } else if (len == 0) {
    synchronized (sessionLock) {
        ensureOpen();
    }
    return;
      }

      while (len > 0) {
    int avail = buffer.remaining();
    if (len <= avail) {
        synchronized (sessionLock) {
      ensureOpen();
        }
        buffer.put(b, off, len);
        return;
    }

    buffer.put(b, off, avail);
    off += avail;
    len -= avail;
    writeBuffer(false);
      }
  }

  public synchronized void flush() throws IOException {
//      synchronized (sessionLock) {
//    ensureOpen();
//      }
//
//      while (buffer.hasRemaining()) {
//    writeBuffer(false);
//      }
  }

  public synchronized void close() throws IOException {
      if (logger.isLoggable(Level.FINEST)) {
    logger.log(Level.FINEST,
         "STACK TRACE", new Throwable("STACK TRACE"));
      }

      synchronized (sessionLock) {
    ensureOpen();
      }

      while (!writeBuffer(true)) { }
  }

  /**
   *
   * This method must ONLY be invoked while synchronized on
   * this session's lock.
   */
  private void ensureOpen() throws IOException {
      assert Thread.holdsLock(sessionLock);

      /*
       * While we're faking that the session is still OK when it really
       * isn't (see above comments), return silently from here.
       */
      if (fakeOKtoWrite) {
    return;
      }

      if (outState > OPEN) {
    if (outState == FINISHED) {
        throw new IOException("stream closed");
    } else {
        throw new IOException("session terminated");
    }
      } else if (sessionDown) {
    IOException ioe = new IOException(sessionDownMessage);
    if (sessionDownCause != null) {
        ioe.initCause(sessionDownCause);
    }
    throw ioe;
      }
  }

  /**
   * Writes as much of the contents of this stream's output buffer
   * as is allowed by the current output ration.  Upon normal return,
   * at least one byte will have been transferred from the buffer to
   * the multiplexed connection output queue, and the buffer will have
   * been compacted, ready to be filled at the current position.
   *
   * Returns true if closeIfComplete and session was marked EOF (with
   * complete buffer written); if true, stream's output buffer should
   * no longer be accessed (because this method will not wait for
   * actual writing of the message).
   */
  private boolean writeBuffer(boolean closeIfComplete)
      throws IOException
  {
      buffer.flip();
      int origLimit = buffer.limit();

      int toSend;
      IOFuture future = null;
      boolean eofSent = false;
      synchronized (sessionLock) {
    while (buffer.remaining() > 0 &&
           !outRationInfinite && outRation < 1 &&
           !sessionDown && outState == OPEN)
    {
        try {
      sessionLock.wait()// REMIND: timeout?
        } catch (InterruptedException e) {
      String message = "request I/O interrupted";
      setDown(message, e);
      IOException ioe = new IOException(message);
      ioe.initCause(e);
      throw ioe;
        }
    }
    ensureOpen();
    assert buffer.remaining() == 0 || outRationInfinite ||
        outRation > 0 || fakeOKtoWrite;

    /*
     * If we're just faking that the session is OK when it really
     * isn't, then we need to stop the writing from proceeding
     * past this barrier-- and if a close was requested, then
     * satisfy it right away.
     */
    if (fakeOKtoWrite) {
        assert role == CLIENT && inState == TERMINATED;
        if (closeIfComplete) {
      fakeOKtoWrite = false;
        }
        buffer.position(origLimit);
        buffer.compact();
        return closeIfComplete;
    }

    boolean complete;
    if (outRationInfinite || buffer.remaining() <= outRation) {
        toSend = buffer.remaining();
        complete = true;
    } else {
        toSend = outRation;
        buffer.limit(toSend);
        complete = false;
    }

    if (!outRationInfinite) {
        outRation -= toSend;
    }
    partialDeliveryStatus = true;

    boolean open = outState == IDLE;
    boolean eof = closeIfComplete && complete;
    boolean close = role == SERVER && eof && inState > OPEN;
    boolean ackRequired = role == SERVER && eof &&
        (ackListeners != null && !ackListeners.isEmpty());

    int op = Mux.Data |
        (open ? Mux.Data_open : 0) |
        (eof ? Mux.Data_eof : 0) |
        (close ? Mux.Data_close : 0) |
        (ackRequired ? Mux.Data_ackRequired : 0);

    /*
     * If we are the server-side, send even the final Data message
     * for this session synchronously with this method, so that the
     * VM will not exit before it gets delivered.  Otherwise, let
     * final Data messages (those with eof true) be sent after this
     * method completes.
     */
    if (!eof || role == SERVER) {
        future = mux.futureSendData(op, sessionID, buffer);
    } else {
        mux.asyncSendData(op, sessionID, buffer);
    }

    if (outState == IDLE) {
        setOutState(OPEN);
        setInState(OPEN);
    }

    if (eof) {
        eofSent = true;
        setOutState(close ? TERMINATED : FINISHED);
        if (ackRequired) {
      sentAckRequired = true;
        }
        sessionLock.notifyAll();
    }
      }

      if (future != null) {
    waitForIO(future);
    buffer.limit(origLimit);    // REMIND: finally?
    buffer.compact();
      }

      return eofSent;
  }

  /**
   *
   * This method must NOT be invoked while synchronized on
   * this session's lock.
   */
  private void waitForIO(IOFuture future) throws IOException {
      assert !Thread.holdsLock(sessionLock);

      try {
    future.waitUntilDone();
      } catch (InterruptedException e) {
    String message = "request I/O interrupted";
    setDown(message, e);
    IOException ioe = new IOException(message);
    ioe.initCause(e);
    throw ioe;
      } catch (IOException e) {
    setDown(e.getMessage(), e.getCause());
    throw e;
      }
  }
    }

    /**
     * Output stream returned by OutboundRequests and InboundRequests for
     * a session of a multiplexed connection.
     */
    private class MuxInputStream extends InputStream {

  MuxInputStream() { }

  public int read() throws IOException {
      synchronized (sessionLock) {
    if (inClosed) {
        throw new IOException("stream closed");
    }

    while (inBufRemaining == 0 &&
           !sessionDown && inState <= OPEN && !inClosed)
    {
        if (inState == IDLE) {
      assert outState == IDLE;
      mux.asyncSendData(Mux.Data | Mux.Data_open,
            sessionID, null);
      setOutState(OPEN);
      setInState(OPEN);
        }
        if (!inRationInfinite && inRation == 0) {
      int inc = mux.initialInboundRation;
      mux.asyncSendIncrementRation(sessionID, inc);
      inRation += inc;
        }
        try {
      sessionLock.wait()// REMIND: timeout?
        } catch (InterruptedException e) {
      String message = "request I/O interrupted";
      setDown(message, e);
      IOException ioe = new IOException(message);
      ioe.initCause(e);
      throw ioe;
        }
    }

    if (inClosed) {
        throw new IOException("stream closed");
    }

    if (inBufRemaining == 0) {
        if (inEOF) {
      return -1;
        } else {
      if (inState == TERMINATED) {
          throw new IOException(
        "request aborted by remote endpoint");
      }
      assert sessionDown;
      IOException ioe = new IOException(sessionDownMessage);
      if (sessionDownCause != null) {
          ioe.initCause(sessionDownCause);
      }
      throw ioe;
        }
    }

    assert inBufQueue.size() > 0;
    int result = -1;
    while (result == -1) {
        ByteBuffer buf = (ByteBuffer) inBufQueue.getFirst();
        if (inBufPos < buf.limit()) {
      result = (buf.get() & 0xFF);
      inBufPos++;
      inBufRemaining--;
        }
        if (inBufPos == buf.limit()) {
      inBufQueue.removeFirst();
      inBufPos = 0;
        }
    }

    if (!inRationInfinite) {
        checkInboundRation();
    }

    return result;
      }
  }

  public int read(byte b[], int off, int len) throws IOException {
      if (b == null) {
    throw new NullPointerException();
      } else if ((off < 0) || (off > b.length) || (len < 0) ||
           ((off + len) > b.length) || ((off + len) < 0))
      {
    throw new IndexOutOfBoundsException();
      }

      synchronized (sessionLock) {
    if (inClosed) {
        throw new IOException("stream closed");
    } else if (len == 0) {
        /*
         * REMIND: What if
         *     - stream is at EOF?
         *     - session was aborted?
         */
        return 0;
    }

    while (inBufRemaining == 0 &&
           !sessionDown && inState <= OPEN && !inClosed)
    {
        if (inState == IDLE) {
      assert outState == IDLE;
      mux.asyncSendData(Mux.Data | Mux.Data_open,
            sessionID, null);
      setOutState(OPEN);
      setInState(OPEN);
        }
        if (!inRationInfinite && inRation == 0) {
      int inc = mux.initialInboundRation;
      mux.asyncSendIncrementRation(sessionID, inc);
      inRation += inc;
        }
        try {
      sessionLock.wait()// REMIND: timeout?
        } catch (InterruptedException e) {
      String message = "request I/O interrupted";
      setDown(message, e);
      IOException ioe = new IOException(message);
      ioe.initCause(e);
      throw ioe;
        }
    }

    if (inClosed) {
        throw new IOException("stream closed");
    }

    if (inBufRemaining == 0) {
        if (inEOF) {
      return -1;
        } else {
      if (inState == TERMINATED) {
          throw new IOException(
        "request aborted by remote endpoint");
      }
      assert sessionDown;
      IOException ioe = new IOException(sessionDownMessage);
      if (sessionDownCause != null) {
          ioe.initCause(sessionDownCause);
      }
      throw ioe;
        }
    }

    assert inBufQueue.size() > 0;
    int remaining = len;
    while (remaining > 0 && inBufRemaining > 0) {
        ByteBuffer buf = (ByteBuffer) inBufQueue.getFirst();
        if (inBufPos < buf.limit()) {
      int toCopy = Math.min(buf.limit() - inBufPos,
                remaining);
      buf.get(b, off, toCopy);
      inBufPos += toCopy;
      inBufRemaining -= toCopy;
      off += toCopy;
      remaining -= toCopy;
        }
        if (inBufPos == buf.limit()) {
      inBufQueue.removeFirst();
      inBufPos = 0;
        }
    }

    if (!inRationInfinite) {
        checkInboundRation();
    }

    return len - remaining;
      }
  }

  /**
   * Sends ration increment, if read drained buffers below
   * a certain mark.
   *
   * This method must NOT be invoked if the inbound ration in
   * infinite, and it must ONLY be invoked while synchronized on
   * this session's lock.
   *
   * REMIND: The implementation of this action will be a
   * significant area for performance tuning.
   */
  private void checkInboundRation() {
      assert Thread.holdsLock(sessionLock);
      assert !inRationInfinite;

      if (inState >= FINISHED) {
    return;
      }
      int mark = mux.initialInboundRation / 2;
      int used = inBufRemaining + inRation;
      if (used <= mark) {
    int inc = mux.initialInboundRation - used;
    mux.asyncSendIncrementRation(sessionID, inc);
    inRation += inc;
      }
  }

  public int available() throws IOException {
      synchronized (sessionLock) {
    if (inClosed) {
        throw new IOException("stream closed");
    }
    /*
     * REMIND: What if
     *     - stream is at EOF?
     *     - session was aborted?
     */
    return inBufRemaining;
      }
  }

  public void close() {
      synchronized (sessionLock) {
    if (inClosed) {
        return;
    }
   
    inClosed = true;
    inBufQueue.clear();    // allow GC of unread data

    if (role == CLIENT && !sentAcknowledgment &&
        receivedAckRequired && outState < TERMINATED)
    {
        mux.asyncSendAcknowledgment(sessionID);
        sentAcknowledgment = true;
        /*
         * If removing this session from the connection's
         * table was delayed in order to be able to send
         * an Acknowledgment message, then take care of
         * removing it now.
         */
        if (removeLater) {
      setOutState(TERMINATED);
      mux.removeSession(sessionID);
      removeLater = false;
        }
    }

    sessionLock.notifyAll();
      }
  }
    }
}
TOP

Related Classes of com.sun.jini.jeri.internal.mux.Session$MuxInputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.