org.xsocket.connection.NonBlockingConnection Maven / Gradle / Ivy
/*
* Copyright (c) xsocket.org, 2006 - 2008. 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.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ReadableByteChannel;
import java.util.Arrays;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLContext;
import org.xsocket.DataConverter;
import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.connection.spi.DefaultIoProvider;
import org.xsocket.connection.spi.IClientIoProvider;
import org.xsocket.connection.spi.IHandlerIoProvider;
import org.xsocket.connection.spi.IIoHandler;
import org.xsocket.connection.spi.IIoHandlerCallback;
/**
* Implementation of the INonBlockingConnection
interface.
*
* A newly created connection is in the open state. Write or rad methods can be called immediately
*
* 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(BlockingConnection.class.getName());
public static final String SEND_TIMEOUT_KEY = "org.xsocket.stream.send_timeout_millis";
public static final long DEFAULT_SEND_TIMEOUT_MILLIS = 60L * 1000L;
private static Executor defaultWorkerPool = null;
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));
}
}
// open flag
private boolean isOpen = true;
// io handler
private static final DefaultIoProvider DEFAULT_CLIENT_IO_PROVIDER = new DefaultIoProvider(); // will be removed
private static IClientIoProvider clientIoProvider = null;
private final IoHandlerCallback ioHandlerCallback = new IoHandlerCallback();
private IHandlerIoProvider ioProvider = null;
private IIoHandler ioHandler = null;
// app handler
private IHandler appHandler = null;
// execution
private Executor workerpool = null;
// write thread handling
private final Object writeSynchronizer = new Object();
// sync flush support
private IOException writeException = null;
private final List pendingWriteConfirmations = new ArrayList();
// timeout support
private boolean idleTimeoutOccured = false;
private boolean connectionTimeoutOccured = false;
private boolean disconnectOccured = false;
// is secured
private boolean isSecured = false;
// cached values
private Integer cachedSoSndBuf = null;
/**
* 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), Integer.MAX_VALUE, new HashMap(), null, false, null, getDefaultWorkerpool());
}
/**
* 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, Integer.MAX_VALUE, new HashMap(), null, false, null, getDefaultWorkerpool());
}
/**
* 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), connectTimeoutMillis, new HashMap(), null, false, null, getDefaultWorkerpool());
}
/**
* 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), Integer.MAX_VALUE, options, null, false, null, getDefaultWorkerpool());
}
/**
* 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), connectTimeoutMillis, options, null, false, null, getDefaultWorkerpool());
}
/**
* 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), Integer.MAX_VALUE, new HashMap(), null, false, appHandler, getDefaultWorkerpool());
}
/**
* 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), connectTimeoutMillis, new HashMap(), null, false, appHandler, getDefaultWorkerpool());
}
/**
* 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), connectTimeoutMillis, new HashMap(), sslContext, sslOn, appHandler, getDefaultWorkerpool());
}
/**
* 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), Integer.MAX_VALUE, options, null, false, appHandler, getDefaultWorkerpool());
}
/**
* 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), connectTimeoutMillis, options, null, false, appHandler, getDefaultWorkerpool());
}
/**
* 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), Integer.MAX_VALUE, new HashMap(), null, false, appHandler, getDefaultWorkerpool());
}
/**
* 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), Integer.MAX_VALUE, new HashMap(), null, false, appHandler, workerPool);
}
/**
* 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), Integer.MAX_VALUE, options, null, false, appHandler, getDefaultWorkerpool());
}
/**
* 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), Integer.MAX_VALUE, new HashMap(), sslContext, sslOn, null, getDefaultWorkerpool());
}
/**
* 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), Integer.MAX_VALUE, options, sslContext, sslOn, null, getDefaultWorkerpool());
}
/**
* 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), Integer.MAX_VALUE, new HashMap(), sslContext, sslOn, null, getDefaultWorkerpool());
}
/**
* 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), Integer.MAX_VALUE, options, sslContext, sslOn, null, getDefaultWorkerpool());
}
/**
* 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), Integer.MAX_VALUE, new HashMap(), null, false, appHandler, workerPool);
}
/**
* 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), connectTimeoutMillis, new HashMap(), null, false, appHandler, workerPool);
}
/**
* 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), connectTimeoutMillis, new HashMap(), sslContext, sslOn, appHandler, workerPool);
}
/**
* 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), Integer.MAX_VALUE, new HashMap(), null, false, appHandler, getDefaultWorkerpool(), autoflush, flushmode);
}
/**
* intermediate client constructor, which uses a specific dispatcher
*/
NonBlockingConnection(InetSocketAddress remoteAddress, int connectTimeoutMillis, Map options, SSLContext sslContext, boolean sslOn, IHandler appHandler, Executor workerpool) throws IOException {
this(remoteAddress, connectTimeoutMillis, options, sslContext, sslOn, appHandler, workerpool, DEFAULT_AUTOFLUSH, DEFAULT_FLUSH_MODE);
}
/**
* client constructor, which uses a specific dispatcher
*/
private NonBlockingConnection(InetSocketAddress remoteAddress, int connectTimeoutMillis, Map options, SSLContext sslContext, boolean isSecured, IHandler appHandler, Executor workerpool, boolean autoflush, FlushMode flushmode) throws IOException {
this.isSecured = isSecured;
setFlushmode(flushmode);
setAutoflush(autoflush);
IIoHandler ioHandler = createClientIoHandler(remoteAddress, connectTimeoutMillis, options, sslContext, isSecured);
init(ioHandler, getClientIoProvider(), HandlerProxy.newPrototype(appHandler, null).newProxy(this), workerpool);
setIdleTimeoutMillis(IConnection.MAX_TIMEOUT_MILLIS);
setConnectionTimeoutMillis(IConnection.MAX_TIMEOUT_MILLIS);
}
/**
* server-side constructor
*/
protected NonBlockingConnection() throws IOException {
}
/**
* returns the default workerpool
*
* @return the default worker pool
*/
synchronized static Executor getDefaultWorkerpool() {
if (defaultWorkerPool == null) {
defaultWorkerPool = Executors.newCachedThreadPool(new DefaultThreadFactory());
}
return defaultWorkerPool;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isMoreInputDataExpected() {
return ioHandler.isOpen();
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isDataWriteable() {
return ioHandler.isOpen();
}
final void init(IIoHandler ioHandler, IHandlerIoProvider ioProvider, IHandler appHandler, Executor workerpool) throws IOException, SocketTimeoutException {
this.ioHandler = ioHandler;
this.ioProvider = ioProvider;
this.appHandler = appHandler;
setWorkerpool(workerpool);
ioHandler.init(ioHandlerCallback);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("connection " + getId() + " created. IoHandler: " + ioHandler.toString());
}
}
/**
* {@inheritDoc}
*/
public final void setHandler(IHandler hdl) throws IOException {
appHandler = HandlerProxy.newPrototype(hdl, null).newProxy(this);
}
/**
* 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;
}
/**
* {@inheritDoc}
*/
@Override
public final void setFlushmode(FlushMode flushMode) {
if (flushMode == FlushMode.ASYNC) {
synchronized (writeSynchronizer) {
if (!pendingWriteConfirmations.isEmpty()) {
LOG.warning("Updating flush mode to " + flushMode + ". A sync flush write operation is currently running which will be updated to async mode");
pendingWriteConfirmations.clear();
ioHandlerCallback.onWritten(null);
}
super.setFlushmode(flushMode);
}
} else {
super.setFlushmode(flushMode);
}
}
/**
* 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 (!pendingWriteConfirmations.isEmpty()) {
return false;
}
writeException = null;
boolean isReset = ioHandler.reset();
if (!isReset) {
return false;
}
return super.reset();
} catch (Exception e) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by reseting connection " + getId() + " " + e.toString());
}
return false;
}
}
/**
* {@inheritDoc}
*/
public final boolean isOpen() {
return isOpen;
}
boolean isConnected() {
return ioHandler.isOpen();
}
/**
* {@inheritDoc}
*/
public final void setWriteTransferRate(int bytesPerSecond) throws ClosedChannelException, IOException {
if (bytesPerSecond != UNLIMITED) {
if (getFlushmode() != FlushMode.ASYNC) {
LOG.warning("setWriteTransferRate is only supported for FlushMode ASYNC. Ignore update of the transfer rate");
return;
}
}
ioHandler = ioProvider.setWriteTransferRate(ioHandler, bytesPerSecond);
}
/**
* {@inheritDoc}
*/
/* public final void setReadTransferRate(int bytesPerSecond) throws ClosedChannelException, IOException {
ioHandler = ioProvider.setReadTransferRate(ioHandler, bytesPerSecond);
}*/
/**
* {@inheritDoc}
*/
public final void activateSecuredMode() throws IOException {
boolean isPrestarted = DEFAULT_CLIENT_IO_PROVIDER.preStartSecuredMode(ioHandler);
if (isPrestarted) {
FlushMode currentFlushMode = getFlushmode();
setFlushmode(FlushMode.ASYNC);
internalFlush();
setFlushmode(currentFlushMode);
ByteBuffer[] buffer = readByteBufferByLength(available());
DEFAULT_CLIENT_IO_PROVIDER.startSecuredMode(ioHandler, buffer);
isSecured = true;
}
}
/**
* {@inheritDoc}
*/
public boolean isSecure() {
return isSecured;
}
/**
* {@inheritDoc}
*/
public final 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 {
ByteBuffer[] buffers = super.readByteBufferByLength(length);
return buffers;
} 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 final void setIdleTimeoutMillis(long timeoutInMillis) {
ioHandler.setIdleTimeoutMillis(timeoutInMillis);
idleTimeoutOccured = false;
}
/**
* {@inheritDoc}
*/
public final void setConnectionTimeoutMillis(long timeoutMillis) {
ioHandler.setConnectionTimeoutMillis(timeoutMillis);
connectionTimeoutOccured = false;
}
/**
* {@inheritDoc}
*/
public long getRemainingMillisToConnectionTimeout() {
return ioHandler.getRemainingMillisToConnectionTimeout();
}
/**
* {@inheritDoc}
*/
public long getRemainingMillisToIdleTimeout() {
return ioHandler.getRemainingMillisToIdleTimeout();
}
/**
* {@inheritDoc}
*/
public final long getConnectionTimeoutMillis() {
return ioHandler.getConnectionTimeoutMillis();
}
/**
* {@inheritDoc}
*
*/
public final long getIdleTimeoutMillis() {
return ioHandler.getIdleTimeoutMillis();
}
/**
* {@inheritDoc}
*/
public final InetAddress getLocalAddress() {
return ioHandler.getLocalAddress();
}
/**
* {@inheritDoc}
*/
public final String getId() {
return ioHandler.getId();
}
/**
* {@inheritDoc}
*/
public final int getLocalPort() {
return ioHandler.getLocalPort();
}
/**
* {@inheritDoc}
*/
public final InetAddress getRemoteAddress() {
return ioHandler.getRemoteAddress();
}
/**
* {@inheritDoc}
*/
public final int getRemotePort() {
return ioHandler.getRemotePort();
}
/**
* {@inheritDoc}
*/
public final int getPendingWriteDataSize() {
return getWriteBufferSize() + ioHandler.getPendingWriteDataSize();
}
/**
* {@inheritDoc}
*/
public final void suspendRead() throws IOException {
ioHandler.suspendRead();
}
/**
* {@inheritDoc}
*/
public final void resumeRead() throws IOException {
ioHandler.resumeRead();
}
/**
* {@inheritDoc}
*/
public long transferFrom(ReadableByteChannel sourceChannel) throws ClosedChannelException, IOException {
int chunkSize = getSoSndBufSize();
return transferFrom(sourceChannel, chunkSize);
}
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();
}
}
/**
* {@inheritDoc}
*/
public final Object getOption(String name) throws IOException {
return ioHandler.getOption(name);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public final 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 = false;
ioHandler.close(true);
} 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 == true) {
isOpen = false;
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("closing connection -> flush all remaining data");
}
if (!isWriteBufferEmpty()) {
ByteBuffer[] buffers = drainWriteQueue();
ioHandler.write(buffers);
}
ioHandler.close(false);
}
}
/**
* {@inheritDoc}
*/
public final void flush() throws ClosedChannelException, IOException {
internalFlush();
}
private void internalFlush() throws ClosedChannelException, IOException {
removeWriteMark();
if (getFlushmode() == FlushMode.SYNC) {
// flush write queue by using a write guard to wait until the onWrittenEvent has been occurred
syncFlush();
} else {
// just flush the queue and return
if (!isWriteBufferEmpty()) {
ByteBuffer[] buffers = drainWriteQueue();
ioHandler.write(buffers);
}
}
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] flushed");
}
}
/**
* flush, and wait until data has been written to channel
*/
@SuppressWarnings("unchecked")
private void syncFlush() throws ClosedChannelException, IOException {
long start = System.currentTimeMillis();
long remainingTime = sendTimeoutMillis;
if (DefaultIoProvider.isDispatcherThread()) {
String msg = "synchronized flushing in NonThreaded mode could cause dead locks (hint: set flush mode to ASYNC)";
LOG.warning(msg);
}
synchronized (writeSynchronizer) {
if (!isWriteBufferEmpty()) {
try {
ByteBuffer[] buffers = drainWriteQueue();
pendingWriteConfirmations.addAll(Arrays.asList(buffers));
ioHandler.write(buffers);
do {
// all data written?
if (pendingWriteConfirmations.isEmpty()) {
return;
// write exception occurred?
} else if(writeException != null) {
IOException ioe = writeException;
writeException = null;
throw ioe;
// ... no -> wait
} else {
try {
writeSynchronizer.wait(remainingTime);
} catch (InterruptedException ignore) { }
}
remainingTime = (start + sendTimeoutMillis) - System.currentTimeMillis();
} while (remainingTime > 0);
throw new SocketTimeoutException("send timeout " + DataConverter.toFormatedDuration(sendTimeoutMillis) + " reached. returning from sync flushing");
} finally {
pendingWriteConfirmations.clear();
}
}
}
}
@SuppressWarnings("unchecked")
private static IClientIoProvider getClientIoProvider() {
if (clientIoProvider == null) {
// IoHandlerManager
String clientIoManagerClassname = System.getProperty(IClientIoProvider.PROVIDER_CLASSNAME_KEY, DefaultIoProvider.class.getName());
try {
Class clientIoManagerClass = Class.forName(clientIoManagerClassname, true, Thread.currentThread().getContextClassLoader());
clientIoProvider = (IClientIoProvider) clientIoManagerClass.newInstance();
} catch (Exception e) {
LOG.warning("error occured by creating ClientIoManager " + clientIoManagerClassname + ": " + e.toString());
LOG.info("using default ClientIoManager " + DEFAULT_CLIENT_IO_PROVIDER.getClass().getName());
clientIoProvider = DEFAULT_CLIENT_IO_PROVIDER;
}
}
return clientIoProvider;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
try {
if (isOpen()) {
return "id=" + getId() + ", remote=" + getRemoteAddress().getCanonicalHostName() + "(" + getRemoteAddress() + ":" + getRemotePort() + ")";
} else {
return "id=" + getId() + " (closed)";
}
} catch (Exception e) {
return super.toString();
}
}
private static IIoHandler createClientIoHandler(InetSocketAddress remoteAddress, int connectTimeoutMillis, Map options, SSLContext sslContext, boolean sslOn) throws IOException {
IIoHandler ioHandler = null;
if (sslContext != null) {
ioHandler = ((DefaultIoProvider) getClientIoProvider()).createSSLClientIoHandler(remoteAddress, connectTimeoutMillis, options, sslContext, sslOn);
} else {
ioHandler = getClientIoProvider().createClientIoHandler(remoteAddress, connectTimeoutMillis, options);
}
return ioHandler;
}
private final class IoHandlerCallback implements IIoHandlerCallback {
public void onWritten(ByteBuffer data) {
if (getFlushmode() == FlushMode.SYNC) {
synchronized (writeSynchronizer) {
if (data != null) {
pendingWriteConfirmations.remove(data);
}
if (pendingWriteConfirmations.isEmpty()) {
writeSynchronizer.notifyAll();
}
}
}
}
public void onWriteException(IOException ioException, ByteBuffer data) {
if (getFlushmode() == FlushMode.SYNC) {
synchronized (writeSynchronizer) {
writeException = ioException;
if (data != null) {
pendingWriteConfirmations.remove(data);
}
writeSynchronizer.notifyAll();
}
}
}
public void onData(ByteBuffer[] data) {
if (LOG.isLoggable(Level.FINE)) {
int size = 0;
for (ByteBuffer byteBuffer : data) {
size += byteBuffer.remaining();
}
LOG.fine("adding " + size + " bytes to receive buffer");
}
appendDataToReadBuffer(data);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("calling app handler " + appHandler);
}
try {
((IDataHandler) appHandler).onData(NonBlockingConnection.this);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by performing onData callback on " + appHandler + " " + ioe.toString());
}
}
}
public void onConnectionAbnormalTerminated() {
NonBlockingConnection.this.forceClose();
}
public void onConnect() {
try {
((IConnectHandler) appHandler).onConnect(NonBlockingConnection.this);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by performing onConnect callback on " + appHandler + " " + ioe.toString());
}
}
}
public void onDisconnect() {
if (!disconnectOccured) {
disconnectOccured = true;
// call first onData
try {
((IDataHandler) appHandler).onData(NonBlockingConnection.this);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by performing onData callback on " + appHandler + " " + ioe.toString());
}
}
// then onDisconnect
try {
((IDisconnectHandler) appHandler).onDisconnect(NonBlockingConnection.this);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by performing onDisconnect callback on " + appHandler + " " + ioe.toString());
}
}
}
}
public void onConnectionTimeout() {
if (!connectionTimeoutOccured) {
connectionTimeoutOccured = true;
try {
((IConnectionTimeoutHandler) appHandler).onConnectionTimeout(NonBlockingConnection.this);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by performing onConnectionTimeout callback on " + appHandler + " " + ioe.toString());
}
}
} else {
setConnectionTimeoutMillis(IConnection.MAX_TIMEOUT_MILLIS);
}
}
public void onIdleTimeout() {
if (!idleTimeoutOccured) {
idleTimeoutOccured = true;
try {
((IIdleTimeoutHandler) appHandler).onIdleTimeout(NonBlockingConnection.this);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by performing onIdleTimeout callback on " + appHandler + " " + ioe.toString());
}
}
} else {
setIdleTimeoutMillis(IConnection.MAX_TIMEOUT_MILLIS);
}
}
}
private static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null)? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = "xNbcPool-" + poolNumber.getAndIncrement() + "-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
if (!t.isDaemon()) {
t.setDaemon(true);
}
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy