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

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

The 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.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
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.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;

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

    /**
     * Additional buffer used for non-blocking writes. Non-blocking writes need
     * to return immediately even if the data cannot be written immediately but
     * the socket buffer may not be big enough to hold all of the unwritten
     * data. This structure provides an additional buffer to hold the data until
     * it can be written.
     * Not that while the Servlet API only allows one non-blocking write at a
     * time, due to buffering and the possible need to write HTTP headers, this
     * layer may see multiple writes.
     */
    protected final WriteBuffer nonBlockingWriteBuffer = new WriteBuffer(bufferedWriteSize);

    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;
    }

    protected AbstractEndpoint getEndpoint() {
        return endpoint;
    }

    /**
     * Transfers processing to a container thread.
     *
     * @param runnable The actions to process on a container thread
     *
     * @throws RejectedExecutionException If the runnable cannot be executed
     */
    public void execute(Runnable runnable) {
        Executor executor = endpoint.getExecutor();
        if (!endpoint.isRunning() || executor == null) {
            throw new RejectedExecutionException();
        }
        executor.execute(runnable);
    }

    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 hasDataToRead() {
        // Return true because it is always safe to make a read attempt
        return true;
    }

    public boolean hasDataToWrite() {
        return !socketBufferHandler.isWriteBufferEmpty() || !nonBlockingWriteBuffer.isEmpty();
    }

    /**
     * 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() && nonBlockingWriteBuffer.isEmpty(); } /** * 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 write buffer. If the socket write * buffer fills during the write, the content of the socket write buffer is * written to the network and this method starts to fill the socket write * buffer again. Depending on the size of the data to write, there may be * multiple writes to the network. *

* Non-blocking writes must return immediately and the byte array holding * the data to be written must be immediately available for re-use. It may * not be possible to write sufficient data to the network to allow this to * happen. In this case data that cannot be written to the network and * cannot be held by the socket buffer is stored in the non-blocking write * buffer. *

* Note: There is an implementation assumption that, before switching from * non-blocking writes to blocking writes, any data remaining in the * non-blocking write buffer will have been written to the network. * * @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. * - To enable a marginally more efficient implemented for blocking * writes which do not require the additional checks related to the * use of the non-blocking write buffer */ if (block) { writeBlocking(buf, off, len); } else { writeNonBlocking(buf, off, len); } } /** * Writes the provided data to the socket write buffer. If the socket write * buffer fills during the write, the content of the socket write buffer is * written to the network and this method starts to fill the socket write * buffer again. Depending on the size of the data to write, there may be * multiple writes to the network. *

* Non-blocking writes must return immediately and the ByteBuffer holding * the data to be written must be immediately available for re-use. It may * not be possible to write sufficient data to the network to allow this to * happen. In this case data that cannot be written to the network and * cannot be held by the socket buffer is stored in the non-blocking write * buffer. *

* Note: There is an implementation assumption that, before switching from * non-blocking writes to blocking writes, any data remaining in the * non-blocking write buffer will have been written to the network. * * @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. * - To enable a marginally more efficient implemented for blocking * writes which do not require the additional checks related to the * use of the non-blocking write buffer */ if (block) { writeBlocking(from); } else { writeNonBlocking(from); } } /** * Writes the provided data to the socket write buffer. If the socket write * buffer fills during the write, the content of the socket write buffer is * written to the network using a blocking write. Once that blocking write * is complete, this method starts to fill the socket write buffer again. * Depending on the size of the data to write, there may be multiple writes * to the network. On completion of this method there will always be space * remaining 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 { 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()); } } /** * Writes the provided data to the socket write buffer. If the socket write * buffer fills during the write, the content of the socket write buffer is * written to the network using a blocking write. Once that blocking write * is complete, this method starts to fill the socket write buffer again. * Depending on the size of the data to write, there may be multiple writes * to the network. On completion of this method there will always be space * remaining in 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 { if (socketBufferHandler.isWriteBufferEmpty()) { // Socket write buffer is empty. Write the provided buffer directly // to the network. // TODO Shouldn't smaller writes be buffered anyway? writeBlockingDirect(from); } else { // Socket write buffer has some data. socketBufferHandler.configureWriteBufferForWrite(); // Put as much data as possible into the write buffer transfer(from, socketBufferHandler.getWriteBuffer()); // If the buffer is now full, write it to the network and then write // the remaining data directly to the network. if (!socketBufferHandler.isWriteBufferWritable()) { doWrite(true); writeBlockingDirect(from); } } } /** * Writes directly to the network, bypassing 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 writeBlockingDirect(ByteBuffer from) throws IOException { // The socket write buffer capacity is socket.appWriteBufSize // TODO This only matters when using TLS. For non-TLS connections it // should be possible to write the ByteBuffer in a single write 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. * After an incomplete write, any data remaining to be transferred to the * socket write buffer will be copied to the socket write buffer. If the * remaining data is too big for the socket write buffer, the socket write * buffer will be filled and the additional data written to the non-blocking * 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 (nonBlockingWriteBuffer.isEmpty() && 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 nonBlockingWriteBuffer.add(buf, off, len); } } /** * 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. * After an incomplete write, any data remaining to be transferred to the * socket write buffer will be copied to the socket write buffer. If the * remaining data is too big for the socket write buffer, the socket write * buffer will be filled and the additional data written to the non-blocking * 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 (nonBlockingWriteBuffer.isEmpty() && socketBufferHandler.isWriteBufferWritable()) { writeNonBlockingInternal(from); } if (from.remaining() > 0) { // Remaining data must be buffered nonBlockingWriteBuffer.add(from); } } /** * Separate method so it can be re-used by the socket write buffer to write * data to the network * * @param from The ByteBuffer containing the data to be written * * @throws IOException If an IO error occurs during the write */ protected void writeNonBlockingInternal(ByteBuffer from) throws IOException { if (socketBufferHandler.isWriteBufferEmpty()) { writeNonBlockingDirect(from); } else { socketBufferHandler.configureWriteBufferForWrite(); transfer(from, socketBufferHandler.getWriteBuffer()); if (!socketBufferHandler.isWriteBufferWritable()) { doWrite(false); if (socketBufferHandler.isWriteBufferWritable()) { writeNonBlockingDirect(from); } } } } protected void writeNonBlockingDirect(ByteBuffer from) throws IOException { // The socket write buffer capacity is socket.appWriteBufSize // TODO This only matters when using TLS. For non-TLS connections it // should be possible to write the ByteBuffer in a single write 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; } } if (from.remaining() > 0) { socketBufferHandler.configureWriteBufferForWrite(); transfer(from, socketBufferHandler.getWriteBuffer()); } } /** * 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 (!nonBlockingWriteBuffer.isEmpty()) { nonBlockingWriteBuffer.write(this, true); 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 && !nonBlockingWriteBuffer.isEmpty()) { dataLeft = nonBlockingWriteBuffer.write(this, false); 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; 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 BlockingMode { /** * The operation will now block. If there are pending operations, * the operation will return CompletionState.NOT_DONE. */ NON_BLOCK, /** * The operation will block until pending operations are completed, but * will not block after performing it. */ SEMI_BLOCK, /** * The operation will block until completed. */ BLOCK } public enum CompletionState { /** * Operation is still pending. */ PENDING, /** * Operation was pending and non blocking. */ NOT_DONE, /** * 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 < length; i++) { if (buffers[offset + i].remaining() > 0) { return CompletionHandlerCall.CONTINUE; } } return (state == CompletionState.DONE) ? CompletionHandlerCall.DONE : CompletionHandlerCall.NONE; } }; /** * This utility CompletionCheck will cause the write to fully write * all remaining data. The completion handler will then be called. */ public static final CompletionCheck COMPLETE_WRITE_WITH_COMPLETION = new CompletionCheck() { @Override public CompletionHandlerCall callHandler(CompletionState state, ByteBuffer[] buffers, int offset, int length) { for (int i = 0; i < length; i++) { if (buffers[offset + i].remaining() > 0) { return CompletionHandlerCall.CONTINUE; } } return CompletionHandlerCall.DONE; } }; /** * 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 */ @Deprecated 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 */ @Deprecated 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 is the blocking mode that will be used for this operation * @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(BlockingMode 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 is the blocking mode that will be used for this operation * @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, BlockingMode 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 is the blocking mode that will be used for this operation * @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(BlockingMode 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 is the blocking mode that will be used for this operation * @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, BlockingMode 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