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

org.xsocket.connection.NonBlockingConnection Maven / Gradle / Ivy

There is a newer version: 2.8.15
Show newest version
/*
 * Copyright (c) xlightweb.org, 2006 - 2010. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
 * The latest copy of this software may be found on http://www.xsocket.org/
 */
package org.xsocket.connection;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.net.ssl.SSLContext;

import org.xsocket.DataConverter;
import org.xsocket.Execution;
import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.SerializedTaskQueue;
import org.xsocket.connection.ConnectionManager.TimeoutMgmHandle;
import org.xsocket.connection.ConnectionUtils.CompletionHandlerInfo;



/**
 * Implementation of the INonBlockingConnection interface. 

* * Depending on the constructor parameter waitForConnect a newly created connection is in the open state. * Write or read methods can be called immediately. * Absence of the waitForConnect parameter is equals to waitForConnect==true. * *

The methods of this class are not thread-safe. * * @author [email protected] */ public final class NonBlockingConnection extends AbstractNonBlockingStream implements INonBlockingConnection { private static final Logger LOG = Logger.getLogger(NonBlockingConnection.class.getName()); public static final String SEND_TIMEOUT_KEY = "org.xsocket.connection.sendFlushTimeoutMillis"; public static final long DEFAULT_SEND_TIMEOUT_MILLIS = 60L * 1000L; private static final boolean IS_SUPPRESS_SYNC_FLUSH_WARNING = IoProvider.getSuppressSyncFlushWarning(); private static final boolean IS_SUPPRESS_SYNC_FLUSH_COMPLITIONHANDLER_WARNING = IoProvider.getSuppressSyncFlushCompletionHandlerWarning(); private static Executor defaultWorkerPool; private static IoConnector defaultConnector; private static long sendTimeoutMillis = DEFAULT_SEND_TIMEOUT_MILLIS; static { try { sendTimeoutMillis = Long.valueOf(System.getProperty(SEND_TIMEOUT_KEY, Long.toString(DEFAULT_SEND_TIMEOUT_MILLIS))); } catch (Exception e) { LOG.warning("invalid value for system property " + SEND_TIMEOUT_KEY + ": " + System.getProperty(SEND_TIMEOUT_KEY) + " (valid is a int value)" + " using default"); sendTimeoutMillis = DEFAULT_SEND_TIMEOUT_MILLIS; } if (LOG.isLoggable(Level.FINE)) { LOG.fine("non blocking connection send time out set with " + DataConverter.toFormatedDuration(sendTimeoutMillis)); } } private final boolean isServerSide; // flags private final AtomicBoolean isOpen = new AtomicBoolean(true); private final AtomicBoolean isConnected = new AtomicBoolean(false); private final AtomicBoolean isSuspended = new AtomicBoolean(false); private final Object disconnectedGuard = false; private boolean isDisconnected = false; // connection manager private static final ConnectionManager DEFAULT_CONNECTION_MANAGER = new ConnectionManager(); private TimeoutMgmHandle timeoutMgmHandle; // io handler private final IoHandlerCallback ioHandlerCallback = new IoHandlerCallback(); private IoChainableHandler ioHandler; // app handler private final AtomicReference handlerAdapterRef = new AtomicReference(null); private final AtomicReference handlerReplaceListenerRef = new AtomicReference(); // execution private Executor workerpool; // task Queue private final SerializedTaskQueue taskQueue = new SerializedTaskQueue(); // write transfer rate private int bytesPerSecond = UNLIMITED; // sync write support private final SynchronWriter synchronWriter = new SynchronWriter(); // write thread handling private final WriteCompletionManager writeCompletionManager = new WriteCompletionManager(); private final Object asyncWriteGuard = new Object(); // timeout support private long idleTimeoutMillis = IConnection.MAX_TIMEOUT_MILLIS; private long idleTimeoutDateMillis = Long.MAX_VALUE; private long connectionTimeoutMillis = IConnection.MAX_TIMEOUT_MILLIS; private long connectionTimeoutDateMillis = Long.MAX_VALUE; private final AtomicBoolean idleTimeoutOccured = new AtomicBoolean(false); private final AtomicBoolean connectionTimeoutOccured = new AtomicBoolean(false); private final AtomicBoolean connectExceptionOccured = new AtomicBoolean(false); // private final AtomicBoolean disconnectOccured = new AtomicBoolean(false); // cached values private Integer cachedSoSndBuf; // max app buffer size private final Object suspendGuard = new Object(); private Integer maxReadBufferSize; /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param hostname the remote host * @param port the port of the remote host to connect * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(String hostname, int port) throws IOException { this(InetAddress.getByName(hostname), port); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote host * @param port the port of the remote host to connect * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port) throws IOException { this(new InetSocketAddress(address, port), true, Integer.MAX_VALUE, new HashMap(), null, false, null, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote host address * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetSocketAddress address) throws IOException { this(address, true, Integer.MAX_VALUE, new HashMap(), null, false, null, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote host * @param port the port of the remote host to connect * @param connectTimeoutMillis the timeout of the connect procedure * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, int connectTimeoutMillis) throws IOException { this(new InetSocketAddress(address, port), true, connectTimeoutMillis, new HashMap(), null, false, null, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote host * @param port the port of the remote host to connect * @param options the socket options * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, Map options) throws IOException { this(new InetSocketAddress(address, port), true, Integer.MAX_VALUE, options, null, false, null, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote host * @param port the port of the remote host to connect * @param connectTimeoutMillis the timeout of the connect procedure * @param options the socket options * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, int connectTimeoutMillis, Map options) throws IOException { this(new InetSocketAddress(address, port), true, connectTimeoutMillis, options, null, false, null, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler) throws IOException { this(new InetSocketAddress(address, port), true, Integer.MAX_VALUE, new HashMap(), null, false, appHandler, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param attachment the attacjment or * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, Object attachment) throws IOException { this(new InetSocketAddress(address, port), true, Integer.MAX_VALUE, new HashMap(), null, false, appHandler, getDefaultWorkerpool(), attachment); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param connectTimeoutMillis the timeout of the connect procedure * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, int connectTimeoutMillis) throws IOException { this(new InetSocketAddress(address, port), true, connectTimeoutMillis, new HashMap(), null, false, appHandler, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param waitForConnect true, if the constructor should block until the connection is established. If the connect fails, the handler's onData and onDisconnect method will be called (if present) * @param connectTimeoutMillis the timeout of the connect procedure * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, boolean waitForConnect, int connectTimeoutMillis) throws IOException { this(new InetSocketAddress(address, port), waitForConnect, connectTimeoutMillis, new HashMap(), null, false, appHandler, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param sslContext the ssl context * @param sslOn true, if ssl should be activated * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, SSLContext sslContext, boolean sslOn) throws IOException { this(new InetSocketAddress(address, port), true, Integer.MAX_VALUE, new HashMap(), sslContext, sslOn, appHandler, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param sslContext the ssl context * @param sslOn true, if ssl should be activated * @param connectTimeoutMillis the timeout of the connect procedure * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, int connectTimeoutMillis, SSLContext sslContext, boolean sslOn) throws IOException { this(new InetSocketAddress(address, port), true, connectTimeoutMillis, new HashMap(), sslContext, sslOn, appHandler, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param sslContext the ssl context * @param sslOn true, if ssl should be activated * @param waitForConnect true, if the constructor should block until the connection is established. If the connect fails, the handler's onData and onDisconnect method will be called (if present) * @param connectTimeoutMillis the timeout of the connect procedure * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, boolean waitForConnect, int connectTimeoutMillis, SSLContext sslContext, boolean sslOn) throws IOException { this(new InetSocketAddress(address, port), waitForConnect, connectTimeoutMillis, new HashMap(), sslContext, sslOn, appHandler, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param options the socket options * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, Map options) throws IOException { this(new InetSocketAddress(address, port), true, Integer.MAX_VALUE, options, null, false, appHandler, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param connectTimeoutMillis the timeout of the connect procedure * @param options the socket options * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, int connectTimeoutMillis, Map options) throws IOException { this(new InetSocketAddress(address, port), true, connectTimeoutMillis, options, null, false, appHandler, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param waitForConnect true, if the constructor should block until the connection is established. If the connect fails, the handler's onData and onDisconnect method will be called (if present) * @param connectTimeoutMillis the timeout of the connect procedure * @param options the socket options * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, boolean waitForConnect, int connectTimeoutMillis, Map options) throws IOException { this(new InetSocketAddress(address, port), waitForConnect, connectTimeoutMillis, options, null, false, appHandler, getDefaultWorkerpool(), null); } /** * constructor

* * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param hostname the remote host * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(String hostname, int port, IHandler appHandler) throws IOException { this(new InetSocketAddress(hostname, port), true, Integer.MAX_VALUE, new HashMap(), null, false, appHandler, getDefaultWorkerpool(), null); } /** * constructor

* * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param hostname the remote host * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param workerPool the worker pool to use * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(String hostname, int port, IHandler appHandler, Executor workerPool) throws IOException { this(new InetSocketAddress(hostname, port), true, Integer.MAX_VALUE, new HashMap(), null, false, appHandler, workerPool, null); } /** * constructor

* * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param hostname the remote host * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param options the socket options * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(String hostname, int port, IHandler appHandler, Map options) throws IOException { this(new InetSocketAddress(hostname, port), true, Integer.MAX_VALUE, options, null, false, appHandler, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param sslContext the ssl context to use * @param sslOn true, activate SSL mode. false, ssl can be activated by user (see {@link IReadWriteableConnection#activateSecuredMode()}) * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, SSLContext sslContext, boolean sslOn) throws IOException { this(new InetSocketAddress(address, port), true, Integer.MAX_VALUE, new HashMap(), sslContext, sslOn, null, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param options the socket options * @param sslContext the ssl context to use * @param sslOn true, activate SSL mode. false, ssl can be activated by user (see {@link IReadWriteableConnection#activateSecuredMode()}) * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, Map options, SSLContext sslContext, boolean sslOn) throws IOException { this(new InetSocketAddress(address, port), true, Integer.MAX_VALUE, options, sslContext, sslOn, null, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param hostname the remote host * @param port the remote port * @param sslContext the ssl context to use * @param sslOn true, activate SSL mode. false, ssl can be activated by user (see {@link IReadWriteableConnection#activateSecuredMode()}) * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(String hostname, int port, SSLContext sslContext, boolean sslOn) throws IOException { this(new InetSocketAddress(hostname, port), true, Integer.MAX_VALUE, new HashMap(), sslContext, sslOn, null, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * * @param hostname the remote host * @param port the remote port * @param options the socket options * @param sslContext the ssl context to use * @param sslOn true, activate SSL mode. false, ssl can be activated by user (see {@link IReadWriteableConnection#activateSecuredMode()}) * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(String hostname, int port, Map options, SSLContext sslContext, boolean sslOn) throws IOException { this(new InetSocketAddress(hostname, port), true, Integer.MAX_VALUE, options, sslContext, sslOn, null, getDefaultWorkerpool(), null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param workerPool the worker pool to use * @throws IOException If some other I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, Executor workerPool) throws IOException { this(new InetSocketAddress(address, port), true, Integer.MAX_VALUE, new HashMap(), null, false, appHandler, workerPool, null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param connectTimeoutMillis the timeout of the connect procedure * @param workerPool the worker pool to use * @throws IOException if a I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, int connectTimeoutMillis, Executor workerPool) throws IOException { this(new InetSocketAddress(address, port), true, connectTimeoutMillis, new HashMap(), null, false, appHandler, workerPool, null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param waitForConnect true, if the constructor should block until the connection is established. If the connect fails, the handler's onData and onDisconnect method will be called (if present) * @param connectTimeoutMillis the timeout of the connect procedure * @param workerPool the worker pool to use * @throws IOException if a I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, boolean waitForConnect, int connectTimeoutMillis, Executor workerPool) throws IOException { this(new InetSocketAddress(address, port), waitForConnect, connectTimeoutMillis, new HashMap(), null, false, appHandler, workerPool, null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler)r * @param connectTimeoutMillis the timeout of the connect procedure * @param sslContext the ssl context to use * @param sslOn true, activate SSL mode. false, ssl can be activated by user (see {@link IReadWriteableConnection#activateSecuredMode()}) * @param workerPool the worker pool to use * @throws IOException if a I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, int connectTimeoutMillis, SSLContext sslContext, boolean sslOn, Executor workerPool) throws IOException { this(new InetSocketAddress(address, port), true, connectTimeoutMillis, new HashMap(), sslContext, sslOn, appHandler, workerPool, null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler)r * @param waitForConnect true, if the constructor should block until the connection is established. If the connect fails, the handler's onData and onDisconnect method will be called (if present) * @param connectTimeoutMillis the timeout of the connect procedure * @param sslContext the ssl context to use * @param sslOn true, activate SSL mode. false, ssl can be activated by user (see {@link IReadWriteableConnection#activateSecuredMode()}) * @param workerPool the worker pool to use * @throws IOException if a I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, boolean waitForConnect, int connectTimeoutMillis, SSLContext sslContext, boolean sslOn, Executor workerPool) throws IOException { this(new InetSocketAddress(address, port), waitForConnect, connectTimeoutMillis, new HashMap(), sslContext, sslOn, appHandler, workerPool, null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param remoteAddress the remote address * @param localAddress the localAddress * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler)r * @param waitForConnect true, if the constructor should block until the connection is established. If the connect fails, the handler's onData and onDisconnect method will be called (if present) * @param connectTimeoutMillis the timeout of the connect procedure * @param options the socket options * @param sslContext the ssl context to use * @param sslOn true, activate SSL mode. false, ssl can be activated by user (see {@link IReadWriteableConnection#activateSecuredMode()}) * @throws IOException if a I/O error occurs */ public NonBlockingConnection(InetSocketAddress remoteAddress, InetSocketAddress localAddress, IHandler appHandler, boolean waitForConnect, int connectTimeoutMillis, Map options, SSLContext sslContext, boolean sslOn) throws IOException { this(remoteAddress, localAddress, waitForConnect, connectTimeoutMillis, options, sslContext, sslOn, appHandler, getDefaultWorkerpool(), DEFAULT_AUTOFLUSH, DEFAULT_FLUSH_MODE, null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param autoflush the auto flush mode * @param flushmode the flush mode * @throws IOException if a I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, boolean autoflush, FlushMode flushmode) throws IOException { this(new InetSocketAddress(address, port), null, true, Integer.MAX_VALUE, new HashMap(), null, false, appHandler, getDefaultWorkerpool(), autoflush, flushmode, null); } /** * constructor. This constructor will be used to create a non blocking * client-side connection.

* * @param address the remote address * @param port the remote port * @param appHandler the application handler (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler and IConnectionTimeoutHandler) * @param autoflush the auto flush mode * @param flushmode the flush mode * @param attachment the attachment or * @throws IOException if a I/O error occurs */ public NonBlockingConnection(InetAddress address, int port, IHandler appHandler, boolean autoflush, FlushMode flushmode, Object attachment) throws IOException { this(new InetSocketAddress(address, port), null, true, Integer.MAX_VALUE, new HashMap(), null, false, appHandler, getDefaultWorkerpool(), autoflush, flushmode, attachment); } /** * intermediate client constructor, which uses a specific dispatcher */ NonBlockingConnection(InetSocketAddress remoteAddress, boolean waitForConnect, int connectTimeoutMillis, Map options, SSLContext sslContext, boolean sslOn, IHandler appHandler, Executor workerpool, Object attachment) throws IOException { this(remoteAddress, null, waitForConnect, connectTimeoutMillis, options, sslContext, sslOn, appHandler, workerpool, DEFAULT_AUTOFLUSH, DEFAULT_FLUSH_MODE, attachment); } /** * client constructor, which uses a specific dispatcher */ private NonBlockingConnection(InetSocketAddress remoteAddress, InetSocketAddress localAddress, boolean waitForConnect, int connectTimeoutMillis, final Map options, final SSLContext sslContext, boolean isSecured, IHandler appHdl, Executor workerpool, boolean autoflush, FlushMode flushmode, Object attachment) throws IOException { setFlushmode(flushmode); setAutoflush(autoflush); setWorkerpool(workerpool); setAttachment(attachment); handlerAdapterRef.set(HandlerAdapter.newInstance(appHdl)); isServerSide = false; SocketChannel channel = openSocket(localAddress, options); IoConnector connector = getDefaultConnector(); // sync connect if (waitForConnect) { SyncIoConnectorCallback callback = new SyncIoConnectorCallback(remoteAddress, channel, sslContext, isSecured, connectTimeoutMillis); connector.connectAsync(channel, remoteAddress, connectTimeoutMillis, callback); callback.connect(); // async connect } else { IIoConnectorCallback callback = new AsyncIoConnectorCallback(remoteAddress, channel, sslContext, isSecured, connectionTimeoutMillis); connector.connectAsync(channel, remoteAddress, connectTimeoutMillis, callback); } } private final class SyncIoConnectorCallback implements IIoConnectorCallback, IIoHandlerCallback { private final AtomicBoolean isConnected = new AtomicBoolean(false); private final InetSocketAddress remoteAddress; private final SocketChannel channel; private final SSLContext sslContext; private final boolean isSecured; private final long connectTimeoutMillis; private boolean isOperationClosed = false; private IOException ioe = null; public SyncIoConnectorCallback(InetSocketAddress remoteAddress, SocketChannel channel, SSLContext sslContext, boolean isSecured, long connectTimeoutMillis) { this.remoteAddress = remoteAddress; this.channel = channel; this.sslContext = sslContext; this.isSecured = isSecured; this.connectTimeoutMillis = connectTimeoutMillis; } void connect() throws IOException { synchronized (this) { while (!isOperationClosed) { try { wait(500); } catch (InterruptedException ie) { // Restore the interrupted status Thread.currentThread().interrupt(); } } } if (ioe != null) { throw ioe; } } private void notifyWaiting() { synchronized (this) { isOperationClosed = true; this.notifyAll(); } } //////////////////////////////////// // IIoConnectorCallback methods public void onConnectionEstablished() throws IOException { assert (ConnectionUtils.isConnectorThread()); register(channel, sslContext, isSecured, this); } public void onConnectError(IOException ioe) { this.ioe = ioe; NonBlockingConnection.this.onConnectException(ioe); notifyWaiting(); } public void onConnectTimeout() { this.ioe = new SocketTimeoutException("connect timeout " + DataConverter.toFormatedDuration(connectTimeoutMillis) + " occured by connecting " + remoteAddress); NonBlockingConnection.this.onConnectException(ioe); notifyWaiting(); } //////////////// // IIoHandlerCallback methods public void onConnect() { if (!isConnected.getAndSet(true)) { // assign the native callback handler ioHandler.setPreviousCallback(ioHandlerCallback); notifyWaiting(); ioHandlerCallback.onConnect(); } } public void onConnectException(IOException ioe) { ioHandlerCallback.onConnectException(ioe); } public void onConnectionAbnormalTerminated() { ioHandlerCallback.onConnectionAbnormalTerminated(); } public void onData(ByteBuffer[] data, int size) { ioHandlerCallback.onData(data, size); } public void onPostData() { ioHandlerCallback.onPostData(); } public void onWritten(ByteBuffer data) { ioHandlerCallback.onWritten(data); } public void onWriteException(IOException ioException, ByteBuffer data) { ioHandlerCallback.onWriteException(ioException, data); } public void onDisconnect() { ioHandlerCallback.onDisconnect(); } } private final class AsyncIoConnectorCallback implements IIoConnectorCallback { private final InetSocketAddress remoteAddress; private final SocketChannel channel; private final SSLContext sslContext; private final boolean isSecured; private final long connectTimeoutMillis; public AsyncIoConnectorCallback(InetSocketAddress remoteAddress, SocketChannel channel, SSLContext sslContext, boolean isSecured, long connectTimeoutMillis) { this.remoteAddress = remoteAddress; this.channel = channel; this.sslContext = sslContext; this.isSecured = isSecured; this.connectTimeoutMillis = connectTimeoutMillis; } public void onConnectionEstablished() throws IOException { register(channel, sslContext, isSecured, ioHandlerCallback); } public void onConnectError(IOException ioe) { onConnectException(ioe); } public void onConnectTimeout() { onConnectException(new SocketTimeoutException("connect timeout " + DataConverter.toFormatedDuration(connectTimeoutMillis) + " occured by connecting " + remoteAddress)); } } /** * server-side constructor */ protected NonBlockingConnection(ConnectionManager connectionManager, HandlerAdapter hdlAdapter) throws IOException { handlerAdapterRef.set(hdlAdapter); isServerSide = true; isConnected.set(true); timeoutMgmHandle = connectionManager.register(this); } /** * will be used for client-side connections only */ private void register(SocketChannel channel, SSLContext sslContext, boolean isSecured, IIoHandlerCallback handleCallback) throws IOException, SocketTimeoutException { IoChainableHandler ioHdl = createClientIoHandler(channel, sslContext, isSecured); timeoutMgmHandle = DEFAULT_CONNECTION_MANAGER.register(this); init(ioHdl, handleCallback); // "true" set of the timeouts setIdleTimeoutMillis(idleTimeoutMillis); setConnectionTimeoutMillis(connectionTimeoutMillis); } SerializedTaskQueue getTaskQueue() { return taskQueue; } long getLastTimeReceivedMillis() { return ioHandler.getLastTimeReceivedMillis(); } long getLastTimeSendMillis() { return ioHandler.getLastTimeSendMillis(); } /** * {@inheritDoc} */ public int getMaxReadBufferThreshold() { if (maxReadBufferSize == null) { return Integer.MAX_VALUE; } else { return maxReadBufferSize; } } /** * {@inheritDoc} */ public void setMaxReadBufferThreshold(int size) { if (size == Integer.MAX_VALUE) { maxReadBufferSize = null; } else { maxReadBufferSize = size; } } /** * {@inheritDoc} */ /*void setMaxWriteBufferThreshold(int size) { if (size == Integer.MAX_VALUE) { maxWriteBufferSize = null; } else { maxWriteBufferSize = size; } }*/ /** * returns the default workerpool * * @return the default worker pool */ synchronized static Executor getDefaultWorkerpool() { if (defaultWorkerPool == null) { defaultWorkerPool = Executors.newCachedThreadPool(new DefaultThreadFactory()); } return defaultWorkerPool; } /** * returns the default connector * * @return the default connector */ private synchronized static IoConnector getDefaultConnector() { if (defaultConnector == null) { defaultConnector = new IoConnector("default"); Thread t = new Thread(defaultConnector); t.setDaemon(true); t.start(); } return defaultConnector; } /** * {@inheritDoc} */ @Override protected boolean isMoreInputDataExpected() { if (ioHandler != null) { return ioHandler.isOpen(); } else { return false; } } /** * {@inheritDoc} */ @Override protected boolean isDataWriteable() { if (ioHandler != null) { return ioHandler.isOpen(); } else { return false; } } void init(IoChainableHandler ioHandler) throws IOException, SocketTimeoutException { init(ioHandler, ioHandlerCallback); } private void init(IoChainableHandler ioHandler, IIoHandlerCallback handlerCallback) throws IOException, SocketTimeoutException { this.ioHandler = ioHandler; ioHandler.init(handlerCallback); isConnected.set(true); if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] connection " + getId() + " created. IoHandler: " + ioHandler.toString()); } } /** * {@inheritDoc} */ public void setHandler(IHandler hdl) throws IOException { HandlerAdapter oldHandlerAdapter = handlerAdapterRef.get(); IHandlerChangeListener changeListener = handlerReplaceListenerRef.get(); // call old handler change listener if ((changeListener != null) && (oldHandlerAdapter != null)) { IHandler oldHandler = oldHandlerAdapter.getHandler(); changeListener.onHanderReplaced(oldHandler, hdl); } boolean isChangeListener = false; if (hdl != null) { isChangeListener = (hdl instanceof IHandlerChangeListener); } HandlerAdapter adapter = HandlerAdapter.newInstance(hdl); boolean callDisconnect = false; synchronized (disconnectedGuard) { handlerAdapterRef.set(adapter); if (isChangeListener) { handlerReplaceListenerRef.set((IHandlerChangeListener) hdl); } if (isDisconnected) { callDisconnect = true; } } // is data available if (getReadQueueSize() > 0) { adapter.onData(NonBlockingConnection.this, taskQueue, workerpool, false, false); } // is disconnected if (callDisconnect) { adapter.onDisconnect(NonBlockingConnection.this, taskQueue, workerpool, false); } } /** * {@inheritDoc} */ public IHandler getHandler() { HandlerAdapter hdlAdapter = handlerAdapterRef.get(); if (hdlAdapter == null) { return null; } else { return hdlAdapter.getHandler(); } } /** * sets the worker pool * @param workerpool the worker pool */ public void setWorkerpool(Executor workerpool) { this.workerpool = workerpool; } /** * gets the workerpool * * @return the workerpool */ public Executor getWorkerpool() { return workerpool; } Executor getExecutor() { return workerpool; } /** * pool support (the connection- and idle timeout and handler will not be reset) * * @return true, if connection has been reset */ protected boolean reset() { try { if (writeCompletionManager.reset() == false) { return false; } if (getReadQueueSize() > 0){ return false; } if (getPendingWriteDataSize() > 0){ return false; } if (!synchronWriter.isReusable()){ return false; } boolean isReset = ioHandler.reset(); if (!isReset) { return false; } return super.reset(); } catch (Exception e) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by reseting connection " + getId() + " " + e.toString()); } return false; } } /** * {@inheritDoc} */ public boolean isOpen() { return isOpen.get(); } public boolean isServerSide() { return isServerSide; } boolean isConnected() { return isConnected.get(); } private void onData(ByteBuffer[] data, int size) { /** * if data is received, the data will be appended to the read buffer by * using the super method appendDataToReadBuffer. Within the super method * the onPostAppend method of this class will be called, before the * super method returns. * * The onPostAppend method implementation checks if the max read buffer * size is reached. If this is true, the receiving will be suspended * * The caller (e.g. IoSocketHandler) will always perform the onPostData method * the after calling this method. Within the onPostData the handler's onData * method will be called */ if (data != null) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] adding " + size + " to read buffer"); } appendDataToReadBuffer(data, size); } } @Override protected void onPostAppend() { /** * this method will be called within the I/O thread after the recevied data * is append to the read buffer. Here it will be check if the max read buffer * size is reached. If this is true, receiving will be suspended * * To avoid race conditions the code is protected by the suspend guard */ // auto suspend support? if (maxReadBufferSize != null) { // check if receiving has to be suspended synchronized (suspendGuard) { if (getReadQueueSize() >= maxReadBufferSize) { try { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] suspending read, because max read buffers size " + maxReadBufferSize + " is execced (" + getReadQueueSize() + ")"); } ioHandler.suspendRead(); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by suspending read (cause by max read queue size " + maxReadBufferSize + " " + ioe.toString()); } } } } } } @Override protected ByteBuffer[] onRead(ByteBuffer[] readBufs) throws IOException { /** * this method will be called after data is read from the read buffer. * If the read buffer size lower than the max read buffer size * the receiving will be resumed (if suspended) * * To avoid race conditions the code is protected by the suspend guard */ // auto suspend support? if (maxReadBufferSize != null) { // check if receiving has to be resumed synchronized (suspendGuard) { if (ioHandler.isReadSuspended() && ((getReadQueueSize() < maxReadBufferSize))) { try { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] resuming read, because read buffer size is lower than max read buffers size " + maxReadBufferSize); } if (!isSuspended.get()) { ioHandler.resumeRead(); } } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by suspending read (cause by max read queue size " + maxReadBufferSize + " " + ioe.toString()); } } } } } return readBufs; } private void onPostData() { /** * This method will be called (by IoSocketHandler) after the onData method * is called */ try { handlerAdapterRef.get().onData(NonBlockingConnection.this, taskQueue, workerpool, false, false); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by performing onData callback on " + handlerAdapterRef.get() + " " + ioe.toString()); } } } private void onWritten(ByteBuffer data) { synchronWriter.onWritten(data); writeCompletionManager.onWritten(data); } private void onWriteException(IOException ioException, ByteBuffer data) { isOpen.set(false); synchronWriter.onException(ioException); writeCompletionManager.onWriteException(ioException, data); } private void onConnect() { try { handlerAdapterRef.get().onConnect(NonBlockingConnection.this, taskQueue, workerpool); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by performing onConnect callback on " + handlerAdapterRef.get() + " " + ioe.toString()); } } } private void onConnectionAbnormalTerminated() { forceClose(); } private void onDisconnect() { HandlerAdapter adapter = null; synchronized (disconnectedGuard) { if (isDisconnected) { return; } else { isDisconnected = true; adapter = handlerAdapterRef.get(); } } isConnected.set(false); TimeoutMgmHandle hdl = timeoutMgmHandle; if (hdl != null) { hdl.destroy(); } if (adapter != null) { // call first onData try { adapter.onData(NonBlockingConnection.this, taskQueue, workerpool, true, false); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by performing onData callback on " + handlerAdapterRef.get() + " " + ioe.toString()); } } // then onDisconnect try { adapter.onDisconnect(NonBlockingConnection.this, taskQueue, workerpool, false); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by performing onDisconnect callback on " + handlerAdapterRef.get() + " " + ioe.toString()); } } } } private void onConnectException(IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("connecting failed " + ioe.toString()); } if (timeoutMgmHandle != null) { timeoutMgmHandle.destroy(); } if (!connectExceptionOccured.getAndSet(true)) { try { handlerAdapterRef.get().onConnectException(NonBlockingConnection.this, taskQueue, workerpool, ioe); } catch (IOException e) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by performing onDisconnect callback on " + handlerAdapterRef.get() + " " + e.toString()); } } } } boolean checkIdleTimeout(Long currentMillis) { if ((idleTimeoutMillis != IConnection.MAX_TIMEOUT_MILLIS) && (getRemainingMillisToIdleTimeout(currentMillis) <= 0)) { onIdleTimeout(); return true; } return false; } void onIdleTimeout() { if (!idleTimeoutOccured.getAndSet(true)) { try { handlerAdapterRef.get().onIdleTimeout(NonBlockingConnection.this, taskQueue, workerpool); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by performing onIdleTimeout callback on " + handlerAdapterRef.get() + " " + ioe.toString()); } } } else { setIdleTimeoutMillis(IConnection.MAX_TIMEOUT_MILLIS); } } boolean checkConnectionTimeout(Long currentMillis) { if ((connectionTimeoutMillis != IConnection.MAX_TIMEOUT_MILLIS) && (getRemainingMillisToConnectionTimeout(currentMillis) <= 0)) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] connection timeout occured"); } onConnectionTimeout(); return true; } return false; } private void onConnectionTimeout() { if (!connectionTimeoutOccured.getAndSet(true)) { try { handlerAdapterRef.get().onConnectionTimeout(NonBlockingConnection.this, taskQueue, workerpool); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by performing onConnectionTimeout callback on " + handlerAdapterRef.get() + " " + ioe.toString()); } } } else { setConnectionTimeoutMillis(IConnection.MAX_TIMEOUT_MILLIS); } } /** * {@inheritDoc} */ public long getRemainingMillisToConnectionTimeout() { return getRemainingMillisToConnectionTimeout(System.currentTimeMillis()); } /** * {@inheritDoc} */ public long getRemainingMillisToIdleTimeout() { return getRemainingMillisToIdleTimeout(System.currentTimeMillis()); } private long getRemainingMillisToConnectionTimeout(long currentMillis) { return connectionTimeoutDateMillis - currentMillis; } public long getNumberOfReceivedBytes() { return ioHandler.getNumberOfReceivedBytes(); } public long getNumberOfSendBytes() { return ioHandler.getNumberOfSendBytes(); } private long getRemainingMillisToIdleTimeout(long currentMillis) { long remaining = idleTimeoutDateMillis - currentMillis; // time out not received if (remaining > 0) { return remaining; // time out received } else { // ... but check if meantime data has been received! return (getLastTimeReceivedMillis() + idleTimeoutMillis) - currentMillis; } } String getRegisteredOpsInfo() { return ioHandler.getRegisteredOpsInfo(); } /** * {@inheritDoc} */ public void setWriteTransferRate(int bytesPerSecond) throws ClosedChannelException, IOException { if ((bytesPerSecond != UNLIMITED) && ((getFlushmode() != FlushMode.ASYNC))) { LOG.warning("setWriteTransferRate is only supported for FlushMode ASYNC. Ignore update of the transfer rate"); return; } if (this.bytesPerSecond == bytesPerSecond) { return; } this.bytesPerSecond = bytesPerSecond; ioHandler = ConnectionUtils.getIoProvider().setWriteTransferRate(ioHandler, bytesPerSecond); } /** * {@inheritDoc} */ public int getWriteTransferRate() throws ClosedChannelException, IOException { return bytesPerSecond; } /** * {@inheritDoc} */ public boolean isSecuredModeActivateable() { return ConnectionUtils.getIoProvider().isSecuredModeActivateable(ioHandler); } /** * {@inheritDoc} */ public void activateSecuredMode() throws IOException { boolean isPrestarted = ConnectionUtils.getIoProvider().preActivateSecuredMode(ioHandler); if (isPrestarted) { FlushMode currentFlushMode = getFlushmode(); setFlushmode(FlushMode.ASYNC); internalFlush(null); setFlushmode(currentFlushMode); ByteBuffer[] buffer = readByteBufferByLength(available()); ConnectionUtils.getIoProvider().activateSecuredMode(ioHandler, buffer); } } public void deactivateSecuredMode() throws IOException { FlushMode currentFlushMode = getFlushmode(); setFlushmode(FlushMode.ASYNC); internalFlush(null); setFlushmode(currentFlushMode); ConnectionUtils.getIoProvider().deactivateSecuredMode(ioHandler); } /** * {@inheritDoc} */ public boolean isSecure() { return ioHandler.isSecure(); } /** * {@inheritDoc} */ public ByteBuffer[] readByteBufferByDelimiter(String delimiter, String encoding, int maxLength) throws IOException, BufferUnderflowException, MaxReadSizeExceededException { try { return super.readByteBufferByDelimiter(delimiter, encoding, maxLength); } catch (MaxReadSizeExceededException mre) { if (isOpen()) { throw mre; } else { throw new ClosedChannelException(); } } catch (BufferUnderflowException bue) { if (isOpen()) { throw bue; } else { throw new ClosedChannelException(); } } } /** * {@inheritDoc} */ public ByteBuffer[] readByteBufferByLength(int length) throws IOException, BufferUnderflowException { try { return super.readByteBufferByLength(length); } catch (BufferUnderflowException bue) { if (isOpen()) { throw bue; } else { throw new ClosedChannelException(); } } } /** * {@inheritDoc} */ protected ByteBuffer readSingleByteBuffer(int length) throws IOException, ClosedChannelException, BufferUnderflowException { try { return super.readSingleByteBuffer(length); } catch (BufferUnderflowException bue) { if (isOpen()) { throw bue; } else { throw new ClosedChannelException(); } } } /** * {@inheritDoc} * */ public void setIdleTimeoutMillis(long timeoutMillis) { idleTimeoutOccured.set(false); if (timeoutMillis <= 0) { LOG.warning("idle timeout " + timeoutMillis + " millis is invalid"); return; } this.idleTimeoutMillis = timeoutMillis; this.idleTimeoutDateMillis = System.currentTimeMillis() + idleTimeoutMillis; if (idleTimeoutDateMillis < 0) { idleTimeoutDateMillis = Long.MAX_VALUE; } if ((timeoutMillis != IConnection.MAX_TIMEOUT_MILLIS) && (isConnected.get())) { long period = idleTimeoutMillis; if (idleTimeoutMillis > 500) { period = idleTimeoutMillis / 5; } timeoutMgmHandle.updateCheckPeriod(period); } } /** * {@inheritDoc} */ public void setConnectionTimeoutMillis(long timeoutMillis) { connectionTimeoutOccured.set(false); if (timeoutMillis <= 0) { LOG.warning("connection timeout " + timeoutMillis + " millis is invalid"); return; } this.connectionTimeoutMillis = timeoutMillis; this.connectionTimeoutDateMillis = System.currentTimeMillis() + connectionTimeoutMillis; if ((timeoutMillis != IConnection.MAX_TIMEOUT_MILLIS) && (isConnected.get())) { long period = connectionTimeoutMillis; if (connectionTimeoutMillis > 500) { period = connectionTimeoutMillis / 5; } timeoutMgmHandle.updateCheckPeriod(period); } } /** * {@inheritDoc} */ public long getConnectionTimeoutMillis() { return connectionTimeoutMillis; } /** * {@inheritDoc} * */ public long getIdleTimeoutMillis() { return idleTimeoutMillis; } /** * {@inheritDoc} */ public InetAddress getLocalAddress() { return ioHandler.getLocalAddress(); } /** * {@inheritDoc} */ public String getId() { return ioHandler.getId(); } /** * {@inheritDoc} */ public int getLocalPort() { return ioHandler.getLocalPort(); } /** * {@inheritDoc} */ public InetAddress getRemoteAddress() { return ioHandler.getRemoteAddress(); } /** * {@inheritDoc} */ public int getRemotePort() { return ioHandler.getRemotePort(); } /** * {@inheritDoc} */ public int getPendingWriteDataSize() { return getWriteBufferSize() + ioHandler.getPendingWriteDataSize(); } /** * {@inheritDoc} */ public void suspendReceiving() throws IOException { synchronized (suspendGuard) { ioHandler.suspendRead(); isSuspended.set(true); } } /** * {@inheritDoc} */ public boolean isReceivingSuspended() { return isSuspended.get(); } /** * {@inheritDoc} */ public void resumeReceiving() throws IOException { synchronized (suspendGuard) { if (isReceivingSuspended()) { ioHandler.resumeRead(); isSuspended.set(false); if (getReadQueueSize() > 0) { ioHandlerCallback.onPostData(); } } } } /** * {@inheritDoc} */ public void write(String message, String encoding, IWriteCompletionHandler writeCompletionHandler) throws IOException { write(DataConverter.toByteBuffer(message, encoding), writeCompletionHandler); } /** * {@inheritDoc} */ public void write(byte[] bytes, IWriteCompletionHandler writeCompletionHandler) throws IOException { write(ByteBuffer.wrap(bytes), writeCompletionHandler); } /** * {@inheritDoc} */ public void write(byte[] bytes, int offset, int length, IWriteCompletionHandler writeCompletionHandler) throws IOException { write(DataConverter.toByteBuffer(bytes, offset, length), writeCompletionHandler); } /** * {@inheritDoc} */ public void write(ByteBuffer[] srcs, int offset, int length, IWriteCompletionHandler writeCompletionHandler) throws IOException { write(DataConverter.toByteBuffers(srcs, offset, length), writeCompletionHandler); } /** * {@inheritDoc} */ public void write(ByteBuffer buffer, IWriteCompletionHandler writeCompletionHandler) throws IOException { if (!IS_SUPPRESS_SYNC_FLUSH_COMPLITIONHANDLER_WARNING && (getFlushmode() == FlushMode.SYNC)) { String msg = "synchronized flush mode/completion handler combination could cause raced conditions (hint: set flush mode to ASYNC). This message can be suppressed by setting system property " + IoProvider.SUPPRESS_SYNC_FLUSH_COMPLETION_HANDLER_WARNING_KEY; LOG.warning("[" + getId() + "] " + msg); } synchronized (asyncWriteGuard) { boolean isSuppressReuseBuffer = isSuppressReuseBufferWarning(); setSuppressReuseBufferWarning(true); writeCompletionManager.registerCompletionHandler(writeCompletionHandler, buffer); write(buffer); setSuppressReuseBufferWarning(isSuppressReuseBuffer); } } /** * {@inheritDoc} */ public void write(ByteBuffer[] buffers, IWriteCompletionHandler writeCompletionHandler) throws IOException { synchronized (asyncWriteGuard) { boolean isSuppressReuseBuffer = isSuppressReuseBufferWarning(); setSuppressReuseBufferWarning(true); writeCompletionManager.registerCompletionHandler(writeCompletionHandler, buffers); write(buffers); setSuppressReuseBufferWarning(isSuppressReuseBuffer); } } /** * {@inheritDoc} */ public void write(List buffers, IWriteCompletionHandler writeCompletionHandler) throws IOException { write(buffers.toArray(new ByteBuffer[buffers.size()]), writeCompletionHandler); } public long transferFrom(ReadableByteChannel source, int chunkSize) throws IOException, BufferOverflowException { if (getFlushmode() == FlushMode.SYNC) { return transferFromSync(source); } else { return super.transferFrom(source, chunkSize); } }; private long transferFromSync(ReadableByteChannel sourceChannel) throws ClosedChannelException, IOException, SocketTimeoutException, ClosedChannelException { if (LOG.isLoggable(Level.FINE)) { LOG.fine("transfering data by using WriteCompletionHandler"); } boolean isSuppressReuseBufferWarning = isSuppressReuseBufferWarning(); try { setSuppressReuseBufferWarning(true); setFlushmode(FlushMode.ASYNC); final Object guard = new Object(); final ByteBuffer copyBuffer = ByteBuffer.allocate(getSoSndBufSize()); final SendTask sendTask = new SendTask(guard, sourceChannel, copyBuffer); sendTask.onWritten(0); synchronized (guard) { if (!sendTask.isComplete()) { try { guard.wait(sendTimeoutMillis); } catch (InterruptedException ie) { // Restore the interrupted status Thread.currentThread().interrupt(); closeSilence(NonBlockingConnection.this); throw new SocketTimeoutException("timeout reached"); } } } if (sendTask.getException() != null) { throw sendTask.getException(); } return sendTask.getWritten(); } finally { setSuppressReuseBufferWarning(isSuppressReuseBufferWarning); setFlushmode(FlushMode.SYNC); } } @Execution(Execution.NONTHREADED) private final class SendTask implements IWriteCompletionHandler { private final Object guard; private final ReadableByteChannel sourceChannel; private final ByteBuffer copyBuffer; private boolean isComplete = false; private long written = 0; private IOException ioe; public SendTask(Object guard, ReadableByteChannel sourceChannel, ByteBuffer copyBuffer) { this.guard = guard; this.sourceChannel = sourceChannel; this.copyBuffer = copyBuffer; } public void onWritten(int written) { if (isComplete) { return; } try { copyBuffer.clear(); int read = sourceChannel.read(copyBuffer); if (read > 0) { copyBuffer.flip(); if (LOG.isLoggable(Level.FINE)) { LOG.fine("writing next chunk (" + copyBuffer.remaining() + " bytes)"); } write(copyBuffer, this); if (!isAutoflush()) { flush(); } } else { synchronized (guard) { isComplete = true; guard.notifyAll(); } } } catch (IOException ioe) { onException(ioe); } } public void onException(IOException ioe) { if (isComplete) { return; } this.ioe = ioe; synchronized (guard) { isComplete = true; guard.notifyAll(); } } IOException getException() { return ioe; } boolean isComplete() { return isComplete; } long getWritten() { return written; } } private int getSoSndBufSize() throws IOException { if (cachedSoSndBuf == null) { cachedSoSndBuf = (Integer) getOption(SO_SNDBUF); } return cachedSoSndBuf; } /** * {@inheritDoc} */ @Override protected void onWriteDataInserted() throws IOException, ClosedChannelException { if (isAutoflush()) { internalFlush(null); } } /** * {@inheritDoc} */ public Object getOption(String name) throws IOException { return ioHandler.getOption(name); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public Map getOptions() { return ioHandler.getOptions(); } /** * {@inheritDoc} */ public void setOption(String name, Object value) throws IOException { if (name.equalsIgnoreCase(SO_SNDBUF)) { cachedSoSndBuf = (Integer) value; } ioHandler.setOption(name, value); } @Override protected int getWriteTransferChunkeSize() { try { return getSoSndBufSize(); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by retrieving SoSndBufSize " + ioe.toString()); } return super.getWriteTransferChunkeSize(); } } private void forceClose() { try { isOpen.set(false); if (ioHandler != null) { ioHandler.close(true); } writeCompletionManager.close(); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("Error occured by closing " + ioe.toString()); } } } /** * {@inheritDoc} */ public void close() throws IOException { super.close(); if (isOpen.getAndSet(false)) { if (getWriteTransferRate() != UNLIMITED) { setWriteTransferRate(UNLIMITED); } synchronWriter.close(); ByteBuffer[] buffers = drainWriteQueue(); if (LOG.isLoggable(Level.FINE)) { if (buffers != null) { LOG.fine("[" + getId() + "] closing connection -> flush all remaining data: " + DataConverter.toString(ConnectionUtils.copy(buffers))); } else { LOG.fine("[" + getId() + "] closing connection (no remaining data)"); } } if (buffers != null) { ioHandler.write(buffers); ioHandler.flush(); } if (ioHandler != null) { ioHandler.close(false); } writeCompletionManager.close(); } } /** * {@inheritDoc} */ static void closeSilence(INonBlockingConnection con) { try { con.close(); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by closing connection " + con.getId() + " " + DataConverter.toString(ioe)); } } } void performMultithreaded(Runnable task) { taskQueue.performMultiThreaded(task, workerpool); } void performNonTHreeaded(Runnable task) { taskQueue.performNonThreaded(task, workerpool); } /** * {@inheritDoc} */ public void flush() throws ClosedChannelException, IOException { internalFlush(null); } /** * {@inheritDoc} */ void flush(List recoveryBuffer) throws ClosedChannelException, IOException { internalFlush(recoveryBuffer); } private void internalFlush(List recoveryBuffer) throws ClosedChannelException, IOException { if (!isOpen.get()) { if (getReadQueueSize() > 0) { throw new ClosedChannelException(); } else { return; } } removeWriteMark(); // is there data to flush? if (!isWriteBufferEmpty()) { // sync flush mode? if (getFlushmode() == FlushMode.SYNC) { synchronWriter.syncWrite(recoveryBuffer); // ... no, async } else { ByteBuffer[] bufs = drainWriteQueue(); if ((bufs != null) && (recoveryBuffer != null)) { for (ByteBuffer buf : bufs) { recoveryBuffer.add(buf.duplicate()); } } ioHandler.write(bufs); ioHandler.flush(); } } if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] flushed"); } } private final class SynchronWriter { private final AtomicBoolean isCallPendingRef = new AtomicBoolean(false); private final ArrayList pendingBuffers = new ArrayList(); private IOException ioe = null; private int countOnWritten = 0; private int countOnException = 0; private int countUnknownOnWritten = 0; boolean isReusable() { synchronized (this) { return !isCallPendingRef.get(); } } void close() { synchronized (this) { pendingBuffers.clear(); } } void onWritten(ByteBuffer data) { if (!isCallPendingRef.get()) { return; } synchronized (this) { countOnWritten++; boolean isRemoved = pendingBuffers.remove(data); if (!isRemoved) { countUnknownOnWritten++; } if (pendingBuffers.isEmpty()) { this.notifyAll(); } } } public void onException(IOException ioe) { if (isCallPendingRef.get()) { return; } synchronized (this) { countOnException++; this.ioe = ioe; pendingBuffers.clear(); this.notifyAll(); } } void syncWrite(List recoveryBuffer) throws IOException, SocketTimeoutException { if (!IS_SUPPRESS_SYNC_FLUSH_WARNING && ConnectionUtils.isDispatcherThread()) { String msg = "synchronized flushing in NonThreaded mode could cause dead locks (hint: set flush mode to ASYNC). This message can be suppressed by setting system property " + IoProvider.SUPPRESS_SYNC_FLUSH_WARNING_KEY; LOG.warning("[" + getId() + "] " + msg); } long start = System.currentTimeMillis(); synchronized (this) { assert (pendingBuffers.isEmpty()) : "no pending buffers should exists"; isCallPendingRef.set(true); // drain the queue ByteBuffer[] data = drainWriteQueue(); if (data == null) { return; } if (recoveryBuffer != null) { for (ByteBuffer buf : data) { recoveryBuffer.add(buf.duplicate()); } } try { // add the data to write into the pending list pendingBuffers.addAll(Arrays.asList(data)); // write the data and flush it ioHandler.write(data); ioHandler.flush(); // wait until the response is received while (true) { // all data written? if (pendingBuffers.isEmpty()) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] data written"); } return; // or got an exception? } else if (ioe != null) { throw ioe; // no, check send timeout } else { long remainingTime = (start + sendTimeoutMillis) - System.currentTimeMillis(); if (remainingTime < 0) { String msg = "[" + getId() + "] send timeout " + DataConverter.toFormatedDuration(sendTimeoutMillis) + " reached. returning from sync flushing (countIsWritten=" + countOnWritten + ", countOnException=" + countOnException + ", sendBytes=" + ioHandler.getNumberOfSendBytes() + ", receivedBytes=" + ioHandler.getNumberOfReceivedBytes() + ", sendQueueSize=" + ioHandler.getPendingWriteDataSize() + ", countUnknownOnWritten=" + countUnknownOnWritten + ", " + ioHandler.getInfo() + ")"; if (LOG.isLoggable(Level.FINE)) { LOG.fine(msg); } throw new SocketTimeoutException(msg); } // wait try { this.wait(remainingTime); } catch (InterruptedException ie) { // Restore the interrupted status Thread.currentThread().interrupt(); } } } } finally { pendingBuffers.clear(); ioe = null; countOnWritten = 0; countOnException = 0; isCallPendingRef.set(false); } } // synchronized } } @Override protected String getInfo() { return toDetailedString(); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder sb = new StringBuilder("id=" + getId()); try { if (isOpen()) { sb.append("remote=" + getRemoteAddress() + "(" + getRemoteAddress() + ":" + getRemotePort() + ")"); } else { sb.append("(closed)"); } } catch (Exception ignore) { } return sb.toString(); } String toDetailedString() { if (isOpen()) { SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss,S"); return "id=" + getId() + ", remote=" + getRemoteAddress().getCanonicalHostName() + "(" + getRemoteAddress() + ":" + getRemotePort() + ") lastTimeReceived=" + df.format(new Date(getLastTimeReceivedMillis())) + " reveived=" + getNumberOfReceivedBytes() + " lastTimeSent=" + df.format(new Date(getLastTimeSendMillis())) + " send=" + getNumberOfSendBytes() + " ops={" + getRegisteredOpsInfo() + "}"; } else { return "id=" + getId() + " (closed)"; } } private static IoChainableHandler createClientIoHandler(SocketChannel channel, SSLContext sslContext, boolean sslOn) throws IOException { if (sslContext != null) { return ConnectionUtils.getIoProvider().createSSLClientIoHandler(channel, sslContext, sslOn); } else { return ConnectionUtils.getIoProvider().createClientIoHandler(channel); } } private static SocketChannel openSocket(InetSocketAddress localAddress, Map options) throws IOException { SocketChannel channel = SocketChannel.open(); for (Entry entry : options.entrySet()) { IoProvider.setOption(channel.socket(), entry.getKey(), entry.getValue()); } if (localAddress != null) { channel.socket().bind(localAddress); } return channel; } private final class IoHandlerCallback implements IIoHandlerCallback { public void onWritten(ByteBuffer data) { NonBlockingConnection.this.onWritten(data); } public void onWriteException(IOException ioException, ByteBuffer data) { NonBlockingConnection.this.onWriteException(ioException, data); } public void onData(ByteBuffer[] data, int size) { NonBlockingConnection.this.onData(data, size); } public void onPostData() { NonBlockingConnection.this.onPostData(); } public void onConnectionAbnormalTerminated() { NonBlockingConnection.this.onConnectionAbnormalTerminated(); } public void onConnect() { NonBlockingConnection.this.onConnect(); } public void onConnectException(IOException ioe) { NonBlockingConnection.this.onConnectException(ioe); } public void onDisconnect() { NonBlockingConnection.this.onDisconnect(); } } private static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1); private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { namePrefix = "xNbcPool-" + POOL_NUMBER.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement()); if (!t.isDaemon()) { t.setDaemon(true); } if (t.getPriority() != Thread.NORM_PRIORITY) { t.setPriority(Thread.NORM_PRIORITY); } return t; } } final class WriteCompletionManager { // write completion handler support private final Map> pendingCompletionConfirmations = new HashMap>(); private AtomicBoolean isWriteCompletionSupportActivated = new AtomicBoolean(false); void registerCompletionHandler(IWriteCompletionHandler writeCompletionHandler, ByteBuffer... buffersToWrite) { WriteCompletionHolder holder = new WriteCompletionHolder(writeCompletionHandler, buffersToWrite); synchronized (this) { isWriteCompletionSupportActivated.set(true); pendingCompletionConfirmations.put(holder, new ArrayList(Arrays.asList(buffersToWrite))); } if (LOG.isLoggable(Level.FINE)) { int size = 0; for (ByteBuffer byteBuffer : buffersToWrite) { size += byteBuffer.remaining(); } LOG.fine("[" + getId() + "] registering " + writeCompletionHandler.getClass().getSimpleName() + "#" + writeCompletionHandler.hashCode() + " waiting for " + size + " bytes"); } } void onWritten(ByteBuffer[] data) { if (isWriteCompletionSupportActivated.get()) { for (ByteBuffer byteBuffer : data) { onWritten(byteBuffer); } } } private void onWritten(ByteBuffer data) { WriteCompletionHolder holderToExecute = null; if (data != null) { synchronized (this) { for (Entry> entry : pendingCompletionConfirmations.entrySet()) { List buffers = entry.getValue(); for (ByteBuffer buf : buffers) { if (buf == data) { buffers.remove(data); break; } } if (buffers.isEmpty()) { holderToExecute = entry.getKey(); pendingCompletionConfirmations.remove(holderToExecute); break; } } } } if (holderToExecute != null) { holderToExecute.performOnWritten(); } } void onWriteException(IOException ioException, ByteBuffer[] data) { if (isWriteCompletionSupportActivated.get()) { for (ByteBuffer byteBuffer : data) { onWriteException(ioException, byteBuffer); } } } private void onWriteException(IOException ioException, ByteBuffer data) { WriteCompletionHolder holderToExecute = null; synchronized (this) { if (data != null) { outer : for (Entry> entry : pendingCompletionConfirmations.entrySet()) { List buffers = entry.getValue(); for (ByteBuffer buf : buffers) { if (buf == data) { holderToExecute = entry.getKey(); pendingCompletionConfirmations.remove(holderToExecute); break outer; } } } } } if (holderToExecute != null) { holderToExecute.performOnException(ioException); } } boolean reset() { synchronized (this) { if (!pendingCompletionConfirmations.isEmpty()) { for (WriteCompletionHolder handler : pendingCompletionConfirmations.keySet()) { handler.callOnException(new ClosedChannelException()); } pendingCompletionConfirmations.clear(); return false; } return true; } } void close() { synchronized (this) { for (WriteCompletionHolder holder : pendingCompletionConfirmations.keySet()) { holder.performOnException(new ExtendedClosedChannelException("[" + getId() + "] is closed")); } } } } private final class WriteCompletionHolder implements Runnable { private final IWriteCompletionHandler handler; private final CompletionHandlerInfo handlerInfo; private final int size; public WriteCompletionHolder(IWriteCompletionHandler handler, ByteBuffer[] bufs) { this.handler = handler; this.handlerInfo = ConnectionUtils.getCompletionHandlerInfo(handler); int l = 0; for (ByteBuffer byteBuffer : bufs) { l += byteBuffer.remaining(); } size = l; } void performOnWritten() { if (handlerInfo.isOnWrittenMultithreaded()) { taskQueue.performMultiThreaded(this, getWorkerpool()); } else { taskQueue.performNonThreaded(this, getWorkerpool()); } } public void run() { callOnWritten(); } private void callOnWritten() { try { handler.onWritten(size); } catch (Exception e) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by calling onWritten " + e.toString() + " closing connection"); } closeSilence(NonBlockingConnection.this); } } void performOnException(final IOException ioe) { if (handlerInfo.isOnExceptionMutlithreaded()) { Runnable task = new Runnable() { public void run() { callOnException(ioe); } }; taskQueue.performMultiThreaded(task, workerpool); } else { Runnable task = new Runnable() { public void run() { callOnException(ioe); } }; taskQueue.performNonThreaded(task, workerpool); } } private void callOnException(IOException ioe) { handler.onException(ioe); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy