All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.tomcat.util.net.SocketWrapperBase Maven / Gradle / Ivy

There is a newer version: 11.0.0-M26
Show newest version
/*
 *  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 org.apache.tomcat.util.net;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.Iterator;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.ByteBufferHolder;
import org.apache.tomcat.util.res.StringManager;

public abstract class SocketWrapperBase {

    private static final Log log = LogFactory.getLog(SocketWrapperBase.class);

    protected static final StringManager sm = StringManager.getManager(SocketWrapperBase.class);

    private final E socket;
    private final AbstractEndpoint endpoint;

    // Volatile because I/O and setting the timeout values occurs on a different
    // thread to the thread checking the timeout.
    private volatile long readTimeout = -1;
    private volatile long writeTimeout = -1;

    private volatile int keepAliveLeft = 100;
    private volatile boolean upgraded = false;
    private boolean secure = false;
    private String negotiatedProtocol = null;
    /*
     * Following cached for speed / reduced GC
     */
    protected String localAddr = null;
    protected String localName = null;
    protected int localPort = -1;
    protected String remoteAddr = null;
    protected String remoteHost = null;
    protected int remotePort = -1;
    /*
     * Used if block/non-blocking is set at the socket level. The client is
     * responsible for the thread-safe use of this field via the locks provided.
     */
    private volatile boolean blockingStatus = true;
    private final Lock blockingStatusReadLock;
    private final WriteLock blockingStatusWriteLock;
    /*
     * Used to record the first IOException that occurs during non-blocking
     * read/writes that can't be usefully propagated up the stack since there is
     * no user code or appropriate container code in the stack to handle it.
     */
    private volatile IOException error = null;

    /**
     * The buffers used for communicating with the socket.
     */
    protected volatile SocketBufferHandler socketBufferHandler = null;

    /**
     * For "non-blocking" writes use an external set of buffers. Although the
     * API only allows one non-blocking write at a time, due to buffering and
     * the possible need to write HTTP headers, there may be more than one write
     * to the OutputBuffer.
     */
    protected final LinkedBlockingDeque bufferedWrites = new LinkedBlockingDeque<>();

    /**
     * The max size of the buffered write buffer
     */
    protected int bufferedWriteSize = 64 * 1024; // 64k default write buffer

    public SocketWrapperBase(E socket, AbstractEndpoint endpoint) {
        this.socket = socket;
        this.endpoint = endpoint;
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        this.blockingStatusReadLock = lock.readLock();
        this.blockingStatusWriteLock = lock.writeLock();
    }

    public E getSocket() {
        return socket;
    }

    public AbstractEndpoint getEndpoint() {
        return endpoint;
    }

    public IOException getError() { return error; }
    public void setError(IOException error) {
        // Not perfectly thread-safe but good enough. Just needs to ensure that
        // once this.error is non-null, it can never be null.
        if (this.error != null) {
            return;
        }
        this.error = error;
    }
    public void checkError() throws IOException {
        if (error != null) {
            throw error;
        }
    }

    public boolean isUpgraded() { return upgraded; }
    public void setUpgraded(boolean upgraded) { this.upgraded = upgraded; }
    public boolean isSecure() { return secure; }
    public void setSecure(boolean secure) { this.secure = secure; }
    public String getNegotiatedProtocol() { return negotiatedProtocol; }
    public void setNegotiatedProtocol(String negotiatedProtocol) {
        this.negotiatedProtocol = negotiatedProtocol;
    }

    /**
     * Set the timeout for reading. Values of zero or less will be changed to
     * -1.
     *
     * @param readTimeout The timeout in milliseconds. A value of -1 indicates
     *                    an infinite timeout.
     */
    public void setReadTimeout(long readTimeout) {
        if (readTimeout > 0) {
            this.readTimeout = readTimeout;
        } else {
            this.readTimeout = -1;
        }
    }

    public long getReadTimeout() {
        return this.readTimeout;
    }

    /**
     * Set the timeout for writing. Values of zero or less will be changed to
     * -1.
     *
     * @param writeTimeout The timeout in milliseconds. A value of zero or less
     *                    indicates an infinite timeout.
     */
    public void setWriteTimeout(long writeTimeout) {
        if (writeTimeout > 0) {
            this.writeTimeout = writeTimeout;
        } else {
            this.writeTimeout = -1;
        }
    }

    public long getWriteTimeout() {
        return this.writeTimeout;
    }


    public void setKeepAliveLeft(int keepAliveLeft) { this.keepAliveLeft = keepAliveLeft;}
    public int decrementKeepAlive() { return (--keepAliveLeft);}

    public String getRemoteHost() {
        if (remoteHost == null) {
            populateRemoteHost();
        }
        return remoteHost;
    }
    protected abstract void populateRemoteHost();

    public String getRemoteAddr() {
        if (remoteAddr == null) {
            populateRemoteAddr();
        }
        return remoteAddr;
    }
    protected abstract void populateRemoteAddr();

    public int getRemotePort() {
        if (remotePort == -1) {
            populateRemotePort();
        }
        return remotePort;
    }
    protected abstract void populateRemotePort();

    public String getLocalName() {
        if (localName == null) {
            populateLocalName();
        }
        return localName;
    }
    protected abstract void populateLocalName();

    public String getLocalAddr() {
        if (localAddr == null) {
            populateLocalAddr();
        }
        return localAddr;
    }
    protected abstract void populateLocalAddr();

    public int getLocalPort() {
        if (localPort == -1) {
            populateLocalPort();
        }
        return localPort;
    }
    protected abstract void populateLocalPort();

    public boolean getBlockingStatus() { return blockingStatus; }
    public void setBlockingStatus(boolean blockingStatus) {
        this.blockingStatus = blockingStatus;
    }
    public Lock getBlockingStatusReadLock() { return blockingStatusReadLock; }
    public WriteLock getBlockingStatusWriteLock() {
        return blockingStatusWriteLock;
    }
    public SocketBufferHandler getSocketBufferHandler() { return socketBufferHandler; }

    public boolean hasDataToWrite() {
        return !socketBufferHandler.isWriteBufferEmpty() || bufferedWrites.size() > 0;
    }

    /**
     * Checks to see if there are any writes pending and if there are calls
     * {@link #registerWriteInterest()} to trigger a callback once the pending
     * writes have completed.
     * 

* Note: Once this method has returned false it MUST NOT * be called again until the pending write has completed and the * callback has been fired. * TODO: Modify {@link #registerWriteInterest()} so the above * restriction is enforced there rather than relying on the caller. * * @return true if no writes are pending and data can be * written otherwise false */ public boolean isReadyForWrite() { boolean result = canWrite(); if (!result) { registerWriteInterest(); } return result; } public boolean canWrite() { if (socketBufferHandler == null) { throw new IllegalStateException(sm.getString("socket.closed")); } return socketBufferHandler.isWriteBufferWritable() && bufferedWrites.size() == 0; } /** * Overridden for debug purposes. No guarantees are made about the format of * this message which may vary significantly between point releases. *

* {@inheritDoc} */ @Override public String toString() { return super.toString() + ":" + String.valueOf(socket); } public abstract int read(boolean block, byte[] b, int off, int len) throws IOException; public abstract int read(boolean block, ByteBuffer to) throws IOException; public abstract boolean isReadyForRead() throws IOException; public abstract void setAppReadBufHandler(ApplicationBufferHandler handler); protected int populateReadBuffer(byte[] b, int off, int len) { socketBufferHandler.configureReadBufferForRead(); ByteBuffer readBuffer = socketBufferHandler.getReadBuffer(); int remaining = readBuffer.remaining(); // Is there enough data in the read buffer to satisfy this request? // Copy what data there is in the read buffer to the byte array if (remaining > 0) { remaining = Math.min(remaining, len); readBuffer.get(b, off, remaining); if (log.isDebugEnabled()) { log.debug("Socket: [" + this + "], Read from buffer: [" + remaining + "]"); } } return remaining; } protected int populateReadBuffer(ByteBuffer to) { // Is there enough data in the read buffer to satisfy this request? // Copy what data there is in the read buffer to the byte array socketBufferHandler.configureReadBufferForRead(); int nRead = transfer(socketBufferHandler.getReadBuffer(), to); if (log.isDebugEnabled()) { log.debug("Socket: [" + this + "], Read from buffer: [" + nRead + "]"); } return nRead; } /** * Return input that has been read to the input buffer for re-reading by the * correct component. There are times when a component may read more data * than it needs before it passes control to another component. One example * of this is during HTTP upgrade. If an (arguably misbehaving client) sends * data associated with the upgraded protocol before the HTTP upgrade * completes, the HTTP handler may read it. This method provides a way for * that data to be returned so it can be processed by the correct component. * * @param returnedInput The input to return to the input buffer. */ public void unRead(ByteBuffer returnedInput) { if (returnedInput != null) { socketBufferHandler.configureReadBufferForWrite(); socketBufferHandler.getReadBuffer().put(returnedInput); } } public abstract void close() throws IOException; public abstract boolean isClosed(); /** * Writes the provided data to the socket, buffering any remaining data if * used in non-blocking mode. * * @param block true if a blocking write should be used, * otherwise a non-blocking write will be used * @param buf The byte array containing the data to be written * @param off The offset within the byte array of the data to be written * @param len The length of the data to be written * * @throws IOException If an IO error occurs during the write */ public final void write(boolean block, byte[] buf, int off, int len) throws IOException { if (len == 0 || buf == null) { return; } // While the implementations for blocking and non-blocking writes are // very similar they have been split into separate methods to allow // sub-classes to override them individually. NIO2, for example, // overrides the non-blocking write but not the blocking write. if (block) { writeBlocking(buf, off, len); } else { writeNonBlocking(buf, off, len); } } /** * Writes the provided data to the socket, buffering any remaining data if * used in non-blocking mode. * * @param block true if a blocking write should be used, * otherwise a non-blocking write will be used * @param from The ByteBuffer containing the data to be written * * @throws IOException If an IO error occurs during the write */ public final void write(boolean block, ByteBuffer from) throws IOException { if (from == null || from.remaining() == 0) { return; } // While the implementations for blocking and non-blocking writes are // very similar they have been split into separate methods to allow // sub-classes to override them individually. NIO2, for example, // overrides the non-blocking write but not the blocking write. if (block) { writeBlocking(from); } else { writeNonBlocking(from); } } /** * Transfers the data to the socket write buffer (writing that data to the * socket if the buffer fills up using a blocking write) until all the data * has been transferred and space remains in the socket write buffer. * * @param buf The byte array containing the data to be written * @param off The offset within the byte array of the data to be written * @param len The length of the data to be written * * @throws IOException If an IO error occurs during the write */ protected void writeBlocking(byte[] buf, int off, int len) throws IOException { // Note: There is an implementation assumption that if the switch from // non-blocking to blocking has been made then any pending // non-blocking writes were flushed at the time the switch // occurred. // Keep writing until all the data has been transferred to the socket // write buffer and space remains in that buffer socketBufferHandler.configureWriteBufferForWrite(); int thisTime = transfer(buf, off, len, socketBufferHandler.getWriteBuffer()); while (socketBufferHandler.getWriteBuffer().remaining() == 0) { len = len - thisTime; off = off + thisTime; doWrite(true); socketBufferHandler.configureWriteBufferForWrite(); thisTime = transfer(buf, off, len, socketBufferHandler.getWriteBuffer()); } } /** * Write the data to the socket (writing that data to the socket using a * blocking write) until all the data has been transferred and space remains * in the socket write buffer. If it is possible use the provided buffer * directly and do not transfer to the socket write buffer. * * @param from The ByteBuffer containing the data to be written * * @throws IOException If an IO error occurs during the write */ protected void writeBlocking(ByteBuffer from) throws IOException { // Note: There is an implementation assumption that if the switch from // non-blocking to blocking has been made then any pending // non-blocking writes were flushed at the time the switch // occurred. // If it is possible write the data to the socket directly from the // provided buffer otherwise transfer it to the socket write buffer if (socketBufferHandler.isWriteBufferEmpty()) { writeByteBufferBlocking(from); } else { socketBufferHandler.configureWriteBufferForWrite(); transfer(from, socketBufferHandler.getWriteBuffer()); if (!socketBufferHandler.isWriteBufferWritable()) { doWrite(true); writeByteBufferBlocking(from); } } } protected void writeByteBufferBlocking(ByteBuffer from) throws IOException { // The socket write buffer capacity is socket.appWriteBufSize int limit = socketBufferHandler.getWriteBuffer().capacity(); int fromLimit = from.limit(); while (from.remaining() >= limit) { from.limit(from.position() + limit); doWrite(true, from); from.limit(fromLimit); } if (from.remaining() > 0) { socketBufferHandler.configureWriteBufferForWrite(); transfer(from, socketBufferHandler.getWriteBuffer()); } } /** * Transfers the data to the socket write buffer (writing that data to the * socket if the buffer fills up using a non-blocking write) until either * all the data has been transferred and space remains in the socket write * buffer or a non-blocking write leaves data in the socket write buffer. * * @param buf The byte array containing the data to be written * @param off The offset within the byte array of the data to be written * @param len The length of the data to be written * * @throws IOException If an IO error occurs during the write */ protected void writeNonBlocking(byte[] buf, int off, int len) throws IOException { if (bufferedWrites.size() == 0 && socketBufferHandler.isWriteBufferWritable()) { socketBufferHandler.configureWriteBufferForWrite(); int thisTime = transfer(buf, off, len, socketBufferHandler.getWriteBuffer()); len = len - thisTime; while (!socketBufferHandler.isWriteBufferWritable()) { off = off + thisTime; doWrite(false); if (len > 0 && socketBufferHandler.isWriteBufferWritable()) { socketBufferHandler.configureWriteBufferForWrite(); thisTime = transfer(buf, off, len, socketBufferHandler.getWriteBuffer()); } else { // Didn't write any data in the last non-blocking write. // Therefore the write buffer will still be full. Nothing // else to do here. Exit the loop. break; } len = len - thisTime; } } if (len > 0) { // Remaining data must be buffered addToBuffers(buf, off, len); } } /** * Writes the data to the socket (writing that data to the socket using a * non-blocking write) until either all the data has been transferred and * space remains in the socket write buffer or a non-blocking write leaves * data in the socket write buffer. If it is possible use the provided * buffer directly and do not transfer to the socket write buffer. * * @param from The ByteBuffer containing the data to be written * * @throws IOException If an IO error occurs during the write */ protected void writeNonBlocking(ByteBuffer from) throws IOException { if (bufferedWrites.size() == 0 && socketBufferHandler.isWriteBufferWritable()) { writeNonBlockingInternal(from); } if (from.remaining() > 0) { // Remaining data must be buffered addToBuffers(from); } } private boolean writeNonBlockingInternal(ByteBuffer from) throws IOException { if (socketBufferHandler.isWriteBufferEmpty()) { return writeByteBufferNonBlocking(from); } else { socketBufferHandler.configureWriteBufferForWrite(); transfer(from, socketBufferHandler.getWriteBuffer()); if (!socketBufferHandler.isWriteBufferWritable()) { doWrite(false); if (socketBufferHandler.isWriteBufferWritable()) { return writeByteBufferNonBlocking(from); } } } return !socketBufferHandler.isWriteBufferWritable(); } protected boolean writeByteBufferNonBlocking(ByteBuffer from) throws IOException { // The socket write buffer capacity is socket.appWriteBufSize int limit = socketBufferHandler.getWriteBuffer().capacity(); int fromLimit = from.limit(); while (from.remaining() >= limit) { int newLimit = from.position() + limit; from.limit(newLimit); doWrite(false, from); from.limit(fromLimit); if (from.position() != newLimit) { // Didn't write the whole amount of data in the last // non-blocking write. // Exit the loop. return true; } } if (from.remaining() > 0) { socketBufferHandler.configureWriteBufferForWrite(); transfer(from, socketBufferHandler.getWriteBuffer()); } return false; } /** * Writes as much data as possible from any that remains in the buffers. * * @param block true if a blocking write should be used, * otherwise a non-blocking write will be used * * @return true if data remains to be flushed after this method * completes, otherwise false. In blocking mode * therefore, the return value should always be false * * @throws IOException If an IO error occurs during the write */ public boolean flush(boolean block) throws IOException { boolean result = false; if (block) { // A blocking flush will always empty the buffer. flushBlocking(); } else { result = flushNonBlocking(); } return result; } protected void flushBlocking() throws IOException { doWrite(true); if (bufferedWrites.size() > 0) { Iterator bufIter = bufferedWrites.iterator(); while (bufIter.hasNext()) { ByteBufferHolder buffer = bufIter.next(); buffer.flip(); writeBlocking(buffer.getBuf()); if (buffer.getBuf().remaining() == 0) { bufIter.remove(); } } if (!socketBufferHandler.isWriteBufferEmpty()) { doWrite(true); } } } protected boolean flushNonBlocking() throws IOException { boolean dataLeft = !socketBufferHandler.isWriteBufferEmpty(); // Write to the socket, if there is anything to write if (dataLeft) { doWrite(false); dataLeft = !socketBufferHandler.isWriteBufferEmpty(); } if (!dataLeft && bufferedWrites.size() > 0) { Iterator bufIter = bufferedWrites.iterator(); while (!dataLeft && bufIter.hasNext()) { ByteBufferHolder buffer = bufIter.next(); buffer.flip(); dataLeft = writeNonBlockingInternal(buffer.getBuf()); if (buffer.getBuf().remaining() == 0) { bufIter.remove(); } } if (!dataLeft && !socketBufferHandler.isWriteBufferEmpty()) { doWrite(false); dataLeft = !socketBufferHandler.isWriteBufferEmpty(); } } return dataLeft; } /** * Write the contents of the socketWriteBuffer to the socket. For blocking * writes either then entire contents of the buffer will be written or an * IOException will be thrown. Partial blocking writes will not occur. * * @param block Should the write be blocking or not? * * @throws IOException If an I/O error such as a timeout occurs during the * write */ protected void doWrite(boolean block) throws IOException { socketBufferHandler.configureWriteBufferForRead(); doWrite(block, socketBufferHandler.getWriteBuffer()); } /** * Write the contents of the ByteBuffer to the socket. For blocking writes * either then entire contents of the buffer will be written or an * IOException will be thrown. Partial blocking writes will not occur. * * @param block Should the write be blocking or not? * @param from the ByteBuffer containing the data to be written * * @throws IOException If an I/O error such as a timeout occurs during the * write */ protected abstract void doWrite(boolean block, ByteBuffer from) throws IOException; protected void addToBuffers(byte[] buf, int offset, int length) { ByteBufferHolder holder = getByteBufferHolder(length); holder.getBuf().put(buf, offset, length); } protected void addToBuffers(ByteBuffer from) { ByteBufferHolder holder = getByteBufferHolder(from.remaining()); holder.getBuf().put(from); } private ByteBufferHolder getByteBufferHolder(int capacity) { ByteBufferHolder holder = bufferedWrites.peekLast(); if (holder == null || holder.isFlipped() || holder.getBuf().remaining() < capacity) { ByteBuffer buffer = ByteBuffer.allocate(Math.max(bufferedWriteSize, capacity)); holder = new ByteBufferHolder(buffer, false); bufferedWrites.add(holder); } return holder; } public void processSocket(SocketEvent socketStatus, boolean dispatch) { endpoint.processSocket(this, socketStatus, dispatch); } public abstract void registerReadInterest(); public abstract void registerWriteInterest(); public abstract SendfileDataBase createSendfileData(String filename, long pos, long length); /** * Starts the sendfile process. It is expected that if the sendfile process * does not complete during this call and does not report an error, that the * caller will not add the socket to the poller (or equivalent). That * is the responsibility of this method. * * @param sendfileData Data representing the file to send * * @return The state of the sendfile process after the first write. */ public abstract SendfileState processSendfile(SendfileDataBase sendfileData); /** * Require the client to perform CLIENT-CERT authentication if it hasn't * already done so. * * @param sslSupport The SSL/TLS support instance currently being used by * the connection that may need updating after the client * authentication * * @throws IOException If authentication is required then there will be I/O * with the client and this exception will be thrown if * that goes wrong */ public abstract void doClientAuth(SSLSupport sslSupport) throws IOException; public abstract SSLSupport getSslSupport(String clientCertProvider); // ------------------------------------------------------- NIO 2 style APIs public enum CompletionState { /** * Operation is still pending. */ PENDING, /** * The operation completed inline. */ INLINE, /** * The operation completed inline but failed. */ ERROR, /** * The operation completed, but not inline. */ DONE } public enum CompletionHandlerCall { /** * Operation should continue, the completion handler shouldn't be * called. */ CONTINUE, /** * The operation completed but the completion handler shouldn't be * called. */ NONE, /** * The operation is complete, the completion handler should be * called. */ DONE } public interface CompletionCheck { /** * Determine what call, if any, should be made to the completion * handler. * * @param state of the operation (done or done in-line since the * IO call is done) * @param buffers ByteBuffer[] that has been passed to the * original IO call * @param offset that has been passed to the original IO call * @param length that has been passed to the original IO call * * @return The call, if any, to make to the completion handler */ public CompletionHandlerCall callHandler(CompletionState state, ByteBuffer[] buffers, int offset, int length); } /** * This utility CompletionCheck will cause the write to fully write * all remaining data. If the operation completes inline, the * completion handler will not be called. */ public static final CompletionCheck COMPLETE_WRITE = new CompletionCheck() { @Override public CompletionHandlerCall callHandler(CompletionState state, ByteBuffer[] buffers, int offset, int length) { for (int i = 0; i < offset; i++) { if (buffers[i].remaining() > 0) { return CompletionHandlerCall.CONTINUE; } } return (state == CompletionState.DONE) ? CompletionHandlerCall.DONE : CompletionHandlerCall.NONE; } }; /** * This utility CompletionCheck will cause the completion handler * to be called once some data has been read. If the operation * completes inline, the completion handler will not be called. */ public static final CompletionCheck READ_DATA = new CompletionCheck() { @Override public CompletionHandlerCall callHandler(CompletionState state, ByteBuffer[] buffers, int offset, int length) { return (state == CompletionState.DONE) ? CompletionHandlerCall.DONE : CompletionHandlerCall.NONE; } }; /** * Allows using NIO2 style read/write only for connectors that can * efficiently support it. * * @return This default implementation always returns {@code false} */ public boolean hasAsyncIO() { return false; } /** * Allows checking if an asynchronous read operation is currently pending. * @return true if the endpoint supports asynchronous IO and * a read operation is being processed asynchronously */ public boolean isReadPending() { return false; } /** * Allows checking if an asynchronous write operation is currently pending. * @return true if the endpoint supports asynchronous IO and * a write operation is being processed asynchronously */ public boolean isWritePending() { return false; } /** * If an asynchronous read operation is pending, this method will block * until the operation completes, or the specified amount of time * has passed. * @param timeout The maximum amount of time to wait * @param unit The unit for the timeout * @return true if the read operation is complete, * false if the operation is still pending and * the specified timeout has passed */ public boolean awaitReadComplete(long timeout, TimeUnit unit) { return true; } /** * If an asynchronous write operation is pending, this method will block * until the operation completes, or the specified amount of time * has passed. * @param timeout The maximum amount of time to wait * @param unit The unit for the timeout * @return true if the read operation is complete, * false if the operation is still pending and * the specified timeout has passed */ public boolean awaitWriteComplete(long timeout, TimeUnit unit) { return true; } /** * Scatter read. The completion handler will be called once some * data has been read or an error occurred. If a CompletionCheck * object has been provided, the completion handler will only be * called if the callHandler method returned true. If no * CompletionCheck object has been provided, the default NIO2 * behavior is used: the completion handler will be called as soon * as some data has been read, even if the read has completed inline. * * @param block true to block until any pending read is done, if the * timeout occurs and a read is still pending, a * ReadPendingException will be thrown; false to * not block but any pending read operation will cause * a ReadPendingException * @param timeout timeout duration for the read * @param unit units for the timeout duration * @param attachment an object to attach to the I/O operation that will be * used when calling the completion handler * @param check for the IO operation completion * @param handler to call when the IO is complete * @param dsts buffers * @param The attachment type * @return the completion state (done, done inline, or still pending) */ public final CompletionState read(boolean block, long timeout, TimeUnit unit, A attachment, CompletionCheck check, CompletionHandler handler, ByteBuffer... dsts) { if (dsts == null) { throw new IllegalArgumentException(); } return read(dsts, 0, dsts.length, block, timeout, unit, attachment, check, handler); } /** * Scatter read. The completion handler will be called once some * data has been read or an error occurred. If a CompletionCheck * object has been provided, the completion handler will only be * called if the callHandler method returned true. If no * CompletionCheck object has been provided, the default NIO2 * behavior is used: the completion handler will be called as soon * as some data has been read, even if the read has completed inline. * * @param dsts buffers * @param offset in the buffer array * @param length in the buffer array * @param block true to block until any pending read is done, if the * timeout occurs and a read is still pending, a * ReadPendingException will be thrown; false to * not block but any pending read operation will cause * a ReadPendingException * @param timeout timeout duration for the read * @param unit units for the timeout duration * @param attachment an object to attach to the I/O operation that will be * used when calling the completion handler * @param check for the IO operation completion * @param handler to call when the IO is complete * @param The attachment type * @return the completion state (done, done inline, or still pending) */ public CompletionState read(ByteBuffer[] dsts, int offset, int length, boolean block, long timeout, TimeUnit unit, A attachment, CompletionCheck check, CompletionHandler handler) { throw new UnsupportedOperationException(); } /** * Gather write. The completion handler will be called once some * data has been written or an error occurred. If a CompletionCheck * object has been provided, the completion handler will only be * called if the callHandler method returned true. If no * CompletionCheck object has been provided, the default NIO2 * behavior is used: the completion handler will be called, even * if the write is incomplete and data remains in the buffers, or * if the write completed inline. * * @param block true to block until any pending write is done, if the * timeout occurs and a write is still pending, a * WritePendingException will be thrown; false to * not block but any pending write operation will cause * a WritePendingException * @param timeout timeout duration for the write * @param unit units for the timeout duration * @param attachment an object to attach to the I/O operation that will be * used when calling the completion handler * @param check for the IO operation completion * @param handler to call when the IO is complete * @param srcs buffers * @param The attachment type * @return the completion state (done, done inline, or still pending) */ public final CompletionState write(boolean block, long timeout, TimeUnit unit, A attachment, CompletionCheck check, CompletionHandler handler, ByteBuffer... srcs) { if (srcs == null) { throw new IllegalArgumentException(); } return write(srcs, 0, srcs.length, block, timeout, unit, attachment, check, handler); } /** * Gather write. The completion handler will be called once some * data has been written or an error occurred. If a CompletionCheck * object has been provided, the completion handler will only be * called if the callHandler method returned true. If no * CompletionCheck object has been provided, the default NIO2 * behavior is used: the completion handler will be called, even * if the write is incomplete and data remains in the buffers, or * if the write completed inline. * * @param srcs buffers * @param offset in the buffer array * @param length in the buffer array * @param block true to block until any pending write is done, if the * timeout occurs and a write is still pending, a * WritePendingException will be thrown; false to * not block but any pending write operation will cause * a WritePendingException * @param timeout timeout duration for the write * @param unit units for the timeout duration * @param attachment an object to attach to the I/O operation that will be * used when calling the completion handler * @param check for the IO operation completion * @param handler to call when the IO is complete * @param The attachment type * @return the completion state (done, done inline, or still pending) */ public CompletionState write(ByteBuffer[] srcs, int offset, int length, boolean block, long timeout, TimeUnit unit, A attachment, CompletionCheck check, CompletionHandler handler) { throw new UnsupportedOperationException(); } // --------------------------------------------------------- Utility methods protected static int transfer(byte[] from, int offset, int length, ByteBuffer to) { int max = Math.min(length, to.remaining()); if (max > 0) { to.put(from, offset, max); } return max; } protected static int transfer(ByteBuffer from, ByteBuffer to) { int max = Math.min(from.remaining(), to.remaining()); if (max > 0) { int fromLimit = from.limit(); from.limit(from.position() + max); to.put(from); from.limit(fromLimit); } return max; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy