
org.xsocket.connection.multiplexed.MultiplexedConnection Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) xsocket.org, 2006 - 2009. 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.multiplexed;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
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.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Map.Entry;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xsocket.DataConverter;
import org.xsocket.Execution;
import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.SerializedTaskQueue;
import org.xsocket.connection.AbstractNonBlockingStream;
import org.xsocket.connection.BlockingConnection;
import org.xsocket.connection.IConnectHandler;
import org.xsocket.connection.IConnection;
import org.xsocket.connection.IConnectionTimeoutHandler;
import org.xsocket.connection.IDataHandler;
import org.xsocket.connection.IDisconnectHandler;
import org.xsocket.connection.IHandler;
import org.xsocket.connection.IIdleTimeoutHandler;
import org.xsocket.connection.INonBlockingConnection;
import org.xsocket.connection.IWriteCompletionHandler;
import org.xsocket.connection.multiplexed.MultiplexedUtils.CompletionHandlerInfo;
import org.xsocket.connection.multiplexed.multiplexer.SimpleMultiplexer;
import org.xsocket.connection.multiplexed.multiplexer.IMultiplexer;
import org.xsocket.connection.multiplexed.multiplexer.IMultiplexer.IDemultiplexResultHandler;
/**
* Implementation of the {@link IMultiplexedConnection}
*
* @author [email protected]
*/
public final class MultiplexedConnection implements IMultiplexedConnection {
private static final Logger LOG = Logger.getLogger(MultiplexedConnection.class.getName());
// timer
private static final Timer TIMER = new Timer("xPipelineTimer", true);
private static final long MIN_WATCHDOG_PERIOD_MILLIS = 30 * 1000;
// pipelines
private final HashMap pipelines = new HashMap();
// underlying connection
private INonBlockingConnection connection = null;
private final Object disconnectedGuard = false;
private boolean isDisconnected = false;
private final AtomicBoolean isOpen = new AtomicBoolean(true);
// multiplexer
private IMultiplexer multiplexer = null;
private final Object multiplexerWriteGuard = new Object();
private final Object multiplexerReadGuard = new Object();
// demultiplex result handler
private final DemultiplexResultHandler demultiplexResultHandler = new DemultiplexResultHandler();
// handler adapter
private PipelineHandlerAdapter handlerAdapter = PipelineHandlerAdapter.newInstance(null);
// task Queue
private final SerializedTaskQueue taskQueue = new SerializedTaskQueue();
// write completion handler support
private final Map> pendingCompletionConfirmations = new HashMap>();
private boolean isWriteCompletionSupportActivated = false;
/**
* constructor. The {@link SimpleMultiplexer} class will be used to (de)multiplex the data
*
* @param connection the underlying connection
* @throws IOException if an exception occurs
*/
public MultiplexedConnection(INonBlockingConnection connection) throws IOException {
this(connection, new SimpleMultiplexer());
}
/**
* constructor.
*
* @param connection the underlying connection
* @param multiplexer the multiplexer to use
* @throws IOException if an exception occurs
*/
public MultiplexedConnection(INonBlockingConnection connection, IMultiplexer multiplexer) throws IOException {
this(connection, PipelineHandlerAdapter.newInstance(null), multiplexer);
}
/**
* constructor.
*
* @param connection the underlying connection
* @param pipelineHandler the pipeline handler
* @param multiplexer the multiplexer to use
* @throws IOException if an exception occurs
*/
public MultiplexedConnection(INonBlockingConnection connection, IHandler pipelineHandler, IMultiplexer multiplexer) throws IOException {
this(connection, PipelineHandlerAdapter.newInstance(pipelineHandler), multiplexer);
}
/**
* constructor. The {@link SimpleMultiplexer} class will be used to (de)multiplex the data
*
* @param connection the underlying connection
* @param pipelineHandler the pipeline handler
* @throws IOException if an exception occurs
*/
public MultiplexedConnection(INonBlockingConnection connection, IHandler pipelineHandler) throws IOException {
this(connection, PipelineHandlerAdapter.newInstance(pipelineHandler), new SimpleMultiplexer());
}
/**
* internal constructor
*
*/
MultiplexedConnection(INonBlockingConnection connection, PipelineHandlerAdapter handlerAdapter, IMultiplexer multiplexer) throws IOException {
this.connection = connection;
this.multiplexer = multiplexer;
this.handlerAdapter = handlerAdapter;
connection.setAutoflush(false);
connection.setFlushmode(FlushMode.SYNC);
connection.setHandler(new MultiplexedConnectionHandler(this));
}
/**
* {@inheritDoc}
*/
public boolean isOpen() {
return (isOpen.get() && connection.isOpen());
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public void close() throws IOException {
if (isOpen.getAndSet(false)) {
HashMap copy = null;
synchronized (pipelines) {
copy = (HashMap) pipelines.clone();
}
for (NonBlockingPipeline pipeline : copy.values()) {
pipeline.closeSilence();
}
try {
connection.close();
} catch (Exception e) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] error occured by closing connection " +e.toString());
}
}
}
}
private void closeSilence() {
try {
close();
} catch (Exception e) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] error occured by closing connection " +e.toString());
}
}
}
/**
* {@inheritDoc}
*/
public String getId() {
return connection.getId();
}
/**
* {@inheritDoc}
*/
public void activateSecuredMode() throws IOException {
synchronized (pipelines) {
for (NonBlockingPipeline pipline : pipelines.values()) {
pipline.flush();
}
}
connection.activateSecuredMode();
}
/**
* {@inheritDoc}
*/
public void deactivateSecuredMode() throws IOException {
synchronized (pipelines) {
for (NonBlockingPipeline pipline : pipelines.values()) {
pipline.flush();
}
}
connection.deactivateSecuredMode();
}
/**
* {@inheritDoc}
*/
public boolean isSecure() {
return connection.isSecure();
}
/**
* {@inheritDoc}
*/
public String getDefaultEncoding() {
return connection.getEncoding();
}
/**
* {@inheritDoc}
*/
public void setDefaultEncoding(String encoding) {
connection.setEncoding(encoding);
}
/**
* {@inheritDoc}
*/
public InetAddress getLocalAddress() {
return connection.getLocalAddress();
}
/**
* {@inheritDoc}
*/
public int getLocalPort() {
return connection.getLocalPort();
}
/**
* {@inheritDoc}
*/
public InetAddress getRemoteAddress() {
return connection.getRemoteAddress();
}
/**
* {@inheritDoc}
*/
public int getRemotePort() {
return connection.getRemotePort();
}
/**
* {@inheritDoc}
*/
public Object getOption(String name) throws IOException {
return connection.getOption(name);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public Map getOptions() {
return connection.getOptions();
}
/**
* {@inheritDoc}
*/
public void setOption(String name, Object value) throws IOException {
connection.setOption(name, value);
}
/**
* {@inheritDoc}
*/
public void setConnectionTimeoutMillis(long timeoutMillis) {
connection.setConnectionTimeoutMillis(timeoutMillis);
}
/**
* {@inheritDoc}
*/
public long getConnectionTimeoutMillis() {
return connection.getConnectionTimeoutMillis();
}
/**
* {@inheritDoc}
*/
public void setIdleTimeoutMillis(long timeoutMillis) {
connection.setIdleTimeoutMillis(timeoutMillis);
}
/**
* {@inheritDoc}
*/
public long getIdleTimeoutMillis() {
return connection.getIdleTimeoutMillis();
}
/**
* {@inheritDoc}
*/
public long getRemainingMillisToConnectionTimeout() {
return connection.getRemainingMillisToConnectionTimeout();
}
/**
* {@inheritDoc}
*/
public long getRemainingMillisToIdleTimeout() {
return connection.getRemainingMillisToIdleTimeout();
}
/**
* {@inheritDoc}
*/
public void setAttachment(Object obj) {
connection.setAttachment(obj);
}
/**
* {@inheritDoc}
*/
public Object getAttachment() {
return connection.getAttachment();
}
/**
* {@inheritDoc}
*/
public boolean isServerSide() {
return connection.isServerSide();
}
/**
* {@inheritDoc}
*/
public String createPipeline() throws IOException {
String pipelineId = registerNewPipeline();
NonBlockingPipeline pipeline = new NonBlockingPipeline(pipelineId, handlerAdapter);
synchronized (pipelines) {
pipelines.put(pipelineId, pipeline);
}
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] pipeline " + pipelineId + " created");
}
return pipelineId;
}
/**
* {@inheritDoc}
*/
public String[] listOpenPipelines() throws ClosedChannelException {
String[] result = null;
synchronized (pipelines) {
Set ids = pipelines.keySet();
result = ids.toArray(new String[ids.size()]);
}
return result;
}
private void closePipeline(NonBlockingPipeline pipeline) throws IOException {
if (pipeline == null) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("warning try to close a pipeline");
}
return;
}
deregisterPipeline(pipeline.getId());
removePipeline(pipeline);
}
private void removePipeline(NonBlockingPipeline pipeline) {
NonBlockingPipeline pipe = null;
synchronized (pipelines) {
pipe = pipelines.remove(pipeline.getId());
}
if (pipe != null) {
pipeline.onClose();
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] pipeline " + pipeline.getId() + " destroyed");
}
}
}
/**
* {@inheritDoc}
*/
public INonBlockingPipeline getNonBlockingPipeline(String pipelineId) throws ClosedChannelException {
if (connection.isOpen()) {
synchronized (pipelines) {
return pipelines.get(pipelineId);
}
} else {
throw new ClosedChannelException();
}
}
/**
* {@inheritDoc}
*/
public IBlockingPipeline getBlockingPipeline(String pipelineId) throws ClosedChannelException, IOException {
return new BlockingPipeline(getNonBlockingPipeline(pipelineId));
}
private void onData() throws IOException {
synchronized (multiplexerReadGuard) {
multiplexer.demultiplex(connection, demultiplexResultHandler);
}
}
private void onPipelineOpened(final String pipelineId) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] pipeline " + pipelineId + " opened by peer");
}
NonBlockingPipeline pipeline = new NonBlockingPipeline(pipelineId, handlerAdapter);
synchronized (pipelines) {
pipelines.put(pipelineId, pipeline);
}
}
private void onPipelineClosed(final String pipelineId) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] pipeline " + pipelineId + " closed by peer");
}
NonBlockingPipeline pipeline = null;
synchronized (pipelines) {
pipeline = pipelines.get(pipelineId);
}
if (pipeline != null) {
removePipeline(pipeline);
}
}
private void onPipelineData(final String pipelineId, ByteBuffer[] data) {
NonBlockingPipeline pipeline = null;
synchronized (pipelines) {
pipeline = pipelines.get(pipelineId);
}
if (pipeline != null) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("notifying pipeline data handler");
}
pipeline.onData(data);
} else {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("data received for non existing pipeline " + pipelineId);
}
}
}
private String registerNewPipeline() throws ClosedChannelException, IOException {
synchronized (multiplexerWriteGuard) {
return multiplexer.openPipeline(connection);
}
}
private void deregisterPipeline(String pipelineId) throws ClosedChannelException, IOException {
synchronized (multiplexerWriteGuard) {
multiplexer.closePipeline(connection, pipelineId);
}
}
private void sendPipelineData(String pipelineId, ByteBuffer[] dataToWrite, FlushMode flushMode, IWriteCompletionHandler completionHandler) throws ClosedChannelException, IOException {
if (dataToWrite == null) {
return;
}
if (dataToWrite.length == 0) {
return;
}
synchronized (multiplexerWriteGuard) {
connection.setFlushmode(flushMode);
multiplexer.multiplex(connection, pipelineId, dataToWrite, completionHandler);
connection.setFlushmode(FlushMode.SYNC);
}
}
private void registerCompletionHandler(IWriteCompletionHandler writeCompletionHandler, boolean isLongResult, ByteBuffer... buffersToWrite) {
isWriteCompletionSupportActivated = true;
WriteCompletionHolder holder = new WriteCompletionHolder(writeCompletionHandler, buffersToWrite);
synchronized (pendingCompletionConfirmations) {
pendingCompletionConfirmations.put(holder, new ArrayList(Arrays.asList( buffersToWrite)));
}
}
private void onWritten(ByteBuffer[] data) {
if (isWriteCompletionSupportActivated) {
for (ByteBuffer byteBuffer : data) {
onWritten(byteBuffer);
}
}
}
private void onWritten(ByteBuffer data) {
WriteCompletionHolder holderToExecute = null;
synchronized (pendingCompletionConfirmations) {
if (data != null) {
for (Entry> entry : pendingCompletionConfirmations.entrySet()) {
List buffers = entry.getValue();
buffers.remove(data);
if (buffers.isEmpty()) {
pendingCompletionConfirmations.remove(entry.getKey());
holderToExecute = entry.getKey();
break;
}
}
}
}
if (holderToExecute != null) {
holderToExecute.performOnWritten();
}
}
private void onWriteException(IOException ioException, ByteBuffer[] data) {
closeSilence();
if (isWriteCompletionSupportActivated) {
for (ByteBuffer byteBuffer : data) {
onWriteException(ioException, byteBuffer);
}
}
}
private void onWriteException(IOException ioException, ByteBuffer data) {
WriteCompletionHolder holderToExecute = null;
synchronized (pendingCompletionConfirmations) {
if (data != null) {
for (Entry> entry : pendingCompletionConfirmations.entrySet()) {
if (entry.getValue().contains(data)) {
pendingCompletionConfirmations.remove(entry.getKey());
holderToExecute = entry.getKey();
break;
}
}
}
}
if (holderToExecute != null) {
holderToExecute.performOnException(ioException);
}
}
private static final class BlockingPipeline extends BlockingConnection implements IBlockingPipeline {
private INonBlockingPipeline delegee = null;
BlockingPipeline(INonBlockingPipeline delegee) throws IOException {
super(delegee);
this.delegee = delegee;
}
public IMultiplexedConnection getMultiplexedConnection() {
return delegee.getMultiplexedConnection();
}
}
private final class DemultiplexResultHandler implements IDemultiplexResultHandler {
public void onPipelineOpend(String pipelineId) {
MultiplexedConnection.this.onPipelineOpened(pipelineId);
}
public void onPipelineClosed(String pipelineId) {
MultiplexedConnection.this.onPipelineClosed(pipelineId);
}
public void onPipelineData(String pipelineId, ByteBuffer[] data) {
MultiplexedConnection.this.onPipelineData(pipelineId, data);
}
}
@Execution(Execution.NONTHREADED)
private static final class MultiplexedConnectionHandler implements IConnectHandler, IDataHandler, IDisconnectHandler, IIdleTimeoutHandler, IConnectionTimeoutHandler {
private MultiplexedConnection multiplexedConnection = null;
private MultiplexedConnectionHandler(MultiplexedConnection multiplexedConnection) {
this.multiplexedConnection = multiplexedConnection;
}
public boolean onConnect(INonBlockingConnection connection) throws IOException, BufferUnderflowException, MaxReadSizeExceededException {
if (connection.available() > 0) {
onData(connection);
}
return true;
}
public boolean onData(INonBlockingConnection connection) throws IOException, BufferUnderflowException, MaxReadSizeExceededException {
multiplexedConnection.onData();
return true;
}
public boolean onDisconnect(INonBlockingConnection connection) throws IOException {
multiplexedConnection.close();
return true;
}
public boolean onIdleTimeout(INonBlockingConnection connection) throws IOException {
multiplexedConnection.close();
return true;
}
public boolean onConnectionTimeout(INonBlockingConnection connection) throws IOException {
multiplexedConnection.close();
return true;
}
}
private final class NonBlockingPipeline extends AbstractNonBlockingStream implements INonBlockingPipeline {
// the pipeline id
private String pipelineId = null;
// close flag
private final AtomicBoolean isOpen = new AtomicBoolean(true);
// suspend support
private boolean isSuspendRead = false;
private final ArrayList suspendBuffer = new ArrayList();
// timeouts
private long connectionTimeoutMillis = IConnection.DEFAULT_CONNECTION_TIMEOUT_MILLIS;
private long idleTimeoutMillis = IConnection.DEFAULT_IDLE_TIMEOUT_MILLIS;
private long idleTimeoutDateMillis = Long.MAX_VALUE;
private long connectionTimeoutDateMillis = Long.MAX_VALUE;
private long lastReceivedMillis = System.currentTimeMillis();
private boolean idleTimeoutOccured = false;
private boolean connectionTimeoutOccured = false;
private WatchDogTask watchDogTask = null;
// handler
private final AtomicReference handlerRef = new AtomicReference(null);
NonBlockingPipeline(String pipelineId, PipelineHandlerAdapter handlerAdapter) {
this.pipelineId = pipelineId;
handlerRef.set(handlerAdapter.getConnectionInstance());
onOpen();
}
private void onOpen() {
try {
handlerRef.get().onConnect(this, taskQueue);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE))
LOG.fine("error occured by performing onConnect call back on " + handlerRef.get() + " " + ioe.toString());
}
}
@Override
protected boolean isDataWriteable() {
try {
return (getNonBlockingPipeline(pipelineId) != null);
} catch (ClosedChannelException ce) {
return false;
}
}
@Override
protected boolean isMoreInputDataExpected() {
try {
return (getNonBlockingPipeline(pipelineId) != null);
} catch (ClosedChannelException ce) {
return false;
}
}
public boolean isOpen() {
if (isOpen.get()) {
if (!isReadBufferEmpty()) {
return true;
}
try {
return (getNonBlockingPipeline(pipelineId) != null);
} catch (ClosedChannelException ce) {
return false;
}
} else {
return false;
}
}
public void close() throws IOException {
super.close();
if (isOpen.getAndSet(false)) {
closePipeline(this);
}
}
private void onClose() {
terminateWatchDog();
try {
handlerRef.get().onDisconnect(this, taskQueue);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE))
LOG.fine("error occured by performing onDisconnect call back on " + handlerRef.get() + " " + ioe.toString());
}
}
public void onData(ByteBuffer[] data) {
if (isSuspendRead) {
for (ByteBuffer byteBuffer : data) {
suspendBuffer.add(byteBuffer);
}
return;
}
int size = 0;
if (data != null) {
for (ByteBuffer byteBuffer : data) {
size += byteBuffer.remaining();
}
}
if (size > 0) {
appendDataToReadBuffer(data, size);
}
try {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("notifying handler " + handlerRef.get());
}
handlerRef.get().onData(this, taskQueue);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by calling onData for pipeline " + getId() + " " + ioe.toString());
}
}
}
private void onConnectionTimeout() {
if (!connectionTimeoutOccured) {
connectionTimeoutOccured = true;
try {
handlerRef.get().onConnectionTimeout(this, taskQueue);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE))
LOG.fine("error occured by performing onConnectionTimeout call back on " + handlerRef.get() + " " + ioe.toString());
}
} else {
setConnectionTimeoutMillis(IConnection.MAX_TIMEOUT_MILLIS);
}
}
private void onIdleTimeout() {
if (!idleTimeoutOccured) {
idleTimeoutOccured = true;
try {
handlerRef.get().onIdleTimeout(this, taskQueue);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE))
LOG.fine("error occured by performing onIdleTimeout call back on " + handlerRef.get() + " " + ioe.toString());
}
} else {
setIdleTimeoutMillis(IConnection.MAX_TIMEOUT_MILLIS);
}
}
void closeSilence() {
try {
close();
} catch (ClosedChannelException cce) {
onClose();
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Error occured by closing connection " + getId() + " " + ioe.toString());
}
}
}
public String getId() {
return pipelineId;
}
public boolean isServerSide() {
return connection.isServerSide();
}
public Executor getWorkerpool() {
return connection.getWorkerpool();
}
public void setWorkerpool(Executor workerpool) {
LOG.warning("setWorkerpool is not supported for a pipeline. perform this operation on the MultiplexedConnection");
}
public IMultiplexedConnection getMultiplexedConnection() {
return MultiplexedConnection.this;
}
public void setMaxReadBufferThreshold(int size) {
throw new UnsupportedOperationException("setMaxReadBufferThreshold is not supported for a pipeline. perform this operation on the MultiplexedConnection");
}
public int getMaxReadBufferThreshold() {
return connection.getMaxReadBufferThreshold();
}
public long getConnectionTimeoutMillis() {
return connectionTimeoutMillis;
}
public void setConnectionTimeoutMillis(long timeoutMillis) {
connectionTimeoutDateMillis = System.currentTimeMillis() + timeoutMillis;
if (connectionTimeoutMillis != timeoutMillis) {
connectionTimeoutMillis = timeoutMillis;
updateWatchdog(connectionTimeoutMillis, idleTimeoutMillis);
}
connectionTimeoutOccured = false;
}
public long getIdleTimeoutMillis() {
return idleTimeoutMillis;
}
public void setIdleTimeoutMillis(long timeoutMillis) {
idleTimeoutDateMillis = System.currentTimeMillis() + timeoutMillis;
if (idleTimeoutMillis != timeoutMillis) {
idleTimeoutMillis = timeoutMillis;
updateWatchdog(connectionTimeoutMillis, idleTimeoutMillis);
}
idleTimeoutOccured = false;
}
/**
* {@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 {
boolean isSuppressReuseBuffer = isSuppressReuseBufferWarning();
setSuppressReuseBufferWarning(true);
registerCompletionHandler(writeCompletionHandler, false, buffer);
write(buffer);
setSuppressReuseBufferWarning(isSuppressReuseBuffer);
}
/**
* {@inheritDoc}
*/
public void write(ByteBuffer[] buffers, IWriteCompletionHandler writeCompletionHandler) throws IOException {
boolean isSuppressReuseBuffer = isSuppressReuseBufferWarning();
setSuppressReuseBufferWarning(true);
registerCompletionHandler(writeCompletionHandler, true, buffers);
write(buffers);
setSuppressReuseBufferWarning(isSuppressReuseBuffer);
}
/**
* {@inheritDoc}
*/
public void write(List buffers, IWriteCompletionHandler writeCompletionHandler) throws IOException {
boolean isSuppressReuseBuffer = isSuppressReuseBufferWarning();
setSuppressReuseBufferWarning(true);
registerCompletionHandler(writeCompletionHandler, true, buffers.toArray(new ByteBuffer[buffers.size()]));
write(buffers);
setSuppressReuseBufferWarning(isSuppressReuseBuffer);
}
private synchronized void updateWatchdog(long connectionTimeoutMillis, long idleTimeoutMillis) {
long watchdogPeriod = connectionTimeoutMillis;
if (idleTimeoutMillis < watchdogPeriod) {
watchdogPeriod = idleTimeoutMillis;
}
if (watchdogPeriod > 500) {
watchdogPeriod = watchdogPeriod / 5;
}
if (watchdogPeriod > MIN_WATCHDOG_PERIOD_MILLIS) {
watchdogPeriod = MIN_WATCHDOG_PERIOD_MILLIS;
}
terminateWatchDog();
watchDogTask = new WatchDogTask(this);
TIMER.schedule(watchDogTask, watchdogPeriod, watchdogPeriod);
}
private synchronized void terminateWatchDog() {
if (watchDogTask != null) {
watchDogTask.cancel();
}
}
private void checkTimeouts() {
long currentMillis = System.currentTimeMillis();
if (getRemainingMillisToConnectionTimeout(currentMillis) <= 0) {
onConnectionTimeout();
}
if (getRemainingMillisToIdleTimeout(currentMillis) <= 0) {
onIdleTimeout();
}
}
/**
* {@inheritDoc}
*/
public long getRemainingMillisToConnectionTimeout() {
return getRemainingMillisToConnectionTimeout(System.currentTimeMillis());
}
private long getRemainingMillisToConnectionTimeout(long currentMillis) {
return connectionTimeoutDateMillis - currentMillis;
}
/**
* {@inheritDoc}
*/
public long getRemainingMillisToIdleTimeout() {
return getRemainingMillisToIdleTimeout(System.currentTimeMillis());
}
private long getRemainingMillisToIdleTimeout(long currentMillis) {
long remaining = idleTimeoutDateMillis - currentMillis;
// time out received
if (remaining > 0) {
return remaining;
// ... yes
} else {
// ... but check if meantime data has been received!
return (lastReceivedMillis + idleTimeoutMillis) - currentMillis;
}
}
public void setWriteTransferRate(int bytesPerSecond) throws ClosedChannelException, IOException {
throw new UnsupportedOperationException("setWriteTransferRate is not supported for a pipeline. perform this operation on the MultiplexedConnection");
}
public int getWriteTransferRate() throws ClosedChannelException, IOException {
return connection.getWriteTransferRate();
}
public void setHandler(IHandler hdl) throws IOException {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] set handler " + hdl);
}
PipelineHandlerAdapter adapter = PipelineHandlerAdapter.newInstance(hdl);
boolean callDisconnect = false;
synchronized (disconnectedGuard) {
handlerRef.set(adapter);
if (isDisconnected) {
callDisconnect = true;
}
}
// calling onConnect
onOpen();
// is data available
if (available() > 0) {
onData(null);
}
// is disconnected
if (callDisconnect) {
close();
}
}
public IHandler getHandler() {
PipelineHandlerAdapter hdlAdapter = handlerRef.get();
if (hdlAdapter == null) {
return null;
} else {
return hdlAdapter.getHandler();
}
}
public void setOption(String name, Object value) throws IOException {
LOG.warning("set option is vaild for all pipelines. Better use .serOptions(String, Object)");
connection.setOption(name, value);
}
public Object getOption(String name) throws IOException {
return connection.getOption(name);
}
@SuppressWarnings("unchecked")
public Map getOptions() {
return connection.getOptions();
}
public InetAddress getLocalAddress() {
return connection.getLocalAddress();
}
public int getLocalPort() {
return connection.getLocalPort();
}
public InetAddress getRemoteAddress() {
return connection.getRemoteAddress();
}
public int getRemotePort() {
return connection.getRemotePort();
}
public void suspendReceiving() throws IOException {
isSuspendRead = true;
}
public boolean isReceivingSuspended() {
return isSuspendRead;
}
@SuppressWarnings("unchecked")
public void resumeReceiving() throws IOException {
isSuspendRead = false;
ArrayList data = (ArrayList) suspendBuffer.clone();
suspendBuffer.clear();
onData(data.toArray(new ByteBuffer[data.size()]));
}
public int getPendingWriteDataSize() {
return getWriteBufferSize();
}
public void activateSecuredMode() throws IOException {
throw new UnsupportedOperationException("activateSecuredMode is not supported for a pipeline. perform this operation on the MultiplexedConnection");
}
public void deactivateSecuredMode() throws IOException {
throw new UnsupportedOperationException("deactivateSecuredMode is not supported for a pipeline. perform this operation on the MultiplexedConnection");
}
public boolean isSecuredModeActivateable() {
return connection.isSecuredModeActivateable();
}
public boolean isSecure() {
return connection.isSecure();
}
public long transferFrom(ReadableByteChannel sourceChannel) throws ClosedChannelException, IOException, SocketTimeoutException {
int chunkSize = (Integer) getOption(SO_SNDBUF);
long transfered = 0;
int read = 0;
do {
ByteBuffer transferBuffer = ByteBuffer.allocate(chunkSize);
read = sourceChannel.read(transferBuffer);
if (read > 0) {
if (transferBuffer.remaining() == 0) {
transferBuffer.flip();
write(transferBuffer);
} else {
transferBuffer.flip();
write(transferBuffer.slice());
}
transfered += read;
}
} while (read > 0);
return transfered;
}
@Override
protected void onWriteDataInserted() throws IOException, ClosedChannelException {
if (isAutoflush()) {
flush();
}
}
public void flush() throws ClosedChannelException, IOException {
removeWriteMark();
final ByteBuffer[] dataToWrite = drainWriteQueue();
IWriteCompletionHandler completionHandler = null;
synchronized (pendingCompletionConfirmations) {
if (!pendingCompletionConfirmations.isEmpty()) {
completionHandler = new IWriteCompletionHandler() {
@Execution(Execution.NONTHREADED)
public void onWritten(int written) throws IOException {
MultiplexedConnection.this.onWritten(dataToWrite);
}
@Execution(Execution.NONTHREADED)
public void onException(IOException ioe) {
MultiplexedConnection.this.onWriteException(ioe, dataToWrite);
}
};
}
}
sendPipelineData(getId(), dataToWrite, getFlushmode(), completionHandler);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
if (isOpen()) {
return "id=" + getId() + ", remote=" + getRemoteAddress().getCanonicalHostName() + "(" + getRemoteAddress() + ":" + getRemotePort() + ")";
} else {
return "id=" + getId() + " (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 = MultiplexedUtils.getCompletionHandlerInfo(handler);
int l = 0;
for (ByteBuffer byteBuffer : bufs) {
l += byteBuffer.remaining();
}
size = l;
}
void performOnWritten() {
if (handlerInfo.isOnWrittenMultithreaded()) {
taskQueue.performMultiThreaded(this, connection.getWorkerpool());
} else {
taskQueue.performNonThreaded(this, connection.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();
}
}
void performOnException(final IOException ioe) {
if (handlerInfo.isOnExceptionMutlithreaded()) {
Runnable task = new Runnable() {
public void run() {
callOnException(ioe);
}
};
taskQueue.performMultiThreaded(task, connection.getWorkerpool());
} else {
Runnable task = new Runnable() {
public void run() {
callOnException(ioe);
}
};
taskQueue.performNonThreaded(task, connection.getWorkerpool());
}
}
private void callOnException(IOException ioe) {
handler.onException(ioe);
}
}
private static final class WatchDogTask extends TimerTask {
private WeakReference nonBlockingPipelineRef = null;
public WatchDogTask(NonBlockingPipeline nonBlockingPipeline) {
nonBlockingPipelineRef = new WeakReference(nonBlockingPipeline);
}
@Override
public void run() {
NonBlockingPipeline nonBlockingPipeline = nonBlockingPipelineRef.get();
if (nonBlockingPipeline == null) {
this.cancel();
} else {
nonBlockingPipeline.checkTimeouts();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy