/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2013 Red Hat, Inc. and/or its affiliates, and individual
* contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xnio;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.xnio.AssertReadWrite.assertReadMessage;
import static org.xnio.AssertReadWrite.assertWrittenMessage;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.jmock.lib.concurrent.DeterministicExecutor;
import org.junit.Test;
import org.xnio.channels.AcceptingChannel;
import org.xnio.mock.AcceptingChannelMock;
import org.xnio.mock.ConnectedStreamChannelMock;
import org.xnio.mock.MessageChannelMock;
import org.xnio.mock.StreamConnectionMock;
/**
* Test for {@link ChannelListeners}.
*
* @author <a href="mailto:frainone@redhat.com">Flavia Rainone</a>
*
*/
public class ChannelListenersTestCase {
@Test
public void invokeChannelListener() {
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
assertTrue(ChannelListeners.invokeChannelListener(channel, listener));
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void invokeChannelListenerWithException() {
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
listener.throwExceptionOnHandle();
assertFalse(ChannelListeners.invokeChannelListener(channel, listener));
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void invokeNullChannelListener() {
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
assertTrue(ChannelListeners.invokeChannelListener(channel, null));
}
@Test
public void invokeChannelListenerWithExecutor() {
final DeterministicExecutor executor = new DeterministicExecutor();
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
ChannelListeners.invokeChannelListener(executor, channel, listener);
assertFalse(listener.isInvoked());
executor.runPendingCommands();
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void invokeChannelListenerWithExecutorAndException() {
final DeterministicExecutor executor = new DeterministicExecutor();
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
listener.throwExceptionOnHandle();
ChannelListeners.invokeChannelListener(executor, channel, listener);
assertFalse(listener.isInvoked());
executor.runPendingCommands();
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void invokeNullChannelListenerWithExecutor() {
final DeterministicExecutor executor = new DeterministicExecutor();
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
ChannelListeners.invokeChannelListener(executor, channel, null);
executor.runPendingCommands();
}
@Test
public void invokeChannelListenerWithRejectedExecution() {
final ExecutionRejector executor = new ExecutionRejector();
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
ChannelListeners.invokeChannelListener(executor, channel, listener);
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void invokeChannelListenerWithRejectedExecutionAndListenerException() {
final ExecutionRejector executor = new ExecutionRejector();
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
listener.throwExceptionOnHandle();
ChannelListeners.invokeChannelListener(executor, channel, listener);
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void invokeNullChannelListenerWithRejectedExecution() {
final ExecutionRejector executor = new ExecutionRejector();
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
ChannelListeners.invokeChannelListener(executor, channel, null);
}
@Test
public void closingChannelListener() {
final ChannelListener<Channel> closingListener = ChannelListeners.closingChannelListener();
assertNotNull(closingListener);
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
assertTrue(channel.isOpen());
closingListener.handleEvent(channel);
assertFalse(channel.isOpen());
// nothing happens
closingListener.handleEvent(null);
}
@Test
public void nullChannelListener() {
final ChannelListener<Channel> nullListener = ChannelListeners.nullChannelListener();
assertNotNull(nullListener);
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
assertTrue(channel.getWrittenText().isEmpty());
assertTrue(channel.isOpen());
// nothing happens
nullListener.handleEvent(channel);
assertTrue(channel.getWrittenText().isEmpty());
assertTrue(channel.isOpen());
nullListener.handleEvent(null);
}
@Test
public void openListenerAdapter() {
final AcceptingChannelMock acceptingChannelMock = new AcceptingChannelMock();
IllegalArgumentException expected = null;
try {
ChannelListeners.openListenerAdapter(null);
} catch (IllegalArgumentException e) {
expected = e;
}
assertNotNull(expected);
final TestChannelListener<StreamConnection> testListener = new TestChannelListener<StreamConnection>();
final ChannelListener<AcceptingChannel<StreamConnection>> acceptingListener = ChannelListeners.openListenerAdapter(testListener);
assertNotNull(acceptingListener);
assertNotNull(acceptingListener.toString());
assertFalse(testListener.isInvoked());
acceptingChannelMock.enableAcceptance(false);
acceptingListener.handleEvent(acceptingChannelMock);
assertFalse(testListener.isInvoked());
acceptingChannelMock.enableAcceptance(true);
acceptingListener.handleEvent(acceptingChannelMock);
assertTrue(testListener.isInvoked());
assertNotNull(testListener.getTargetChannel());
final AcceptingChannelMock failingAcceptingChannel = new AcceptingChannelMock() {
@Override
public StreamConnectionMock accept() throws IOException {
throw new IOException("Test exception");
}
};
// nothing should happen
acceptingListener.handleEvent(failingAcceptingChannel);
}
@Test
public void channelListenerSetterByFieldUpdater() {
final ListenerSetterTesterChannel channel = new ListenerSetterTesterChannel();
@SuppressWarnings("rawtypes")
final AtomicReferenceFieldUpdater<ListenerSetterTesterChannel, ChannelListener> fieldUpdater =
channel.getFieldUpdater();
final TestChannelListener<ConnectedStreamChannelMock> listener1 = new TestChannelListener<ConnectedStreamChannelMock>();
final TestChannelListener<ConnectedStreamChannelMock> listener2 = new TestChannelListener<ConnectedStreamChannelMock>();
final TestChannelListener<ConnectedStreamChannelMock> listener3 = new TestChannelListener<ConnectedStreamChannelMock>();
@SuppressWarnings("deprecation")
final ChannelListener.Setter<ConnectedStreamChannelMock> setter = ChannelListeners.getSetter(channel, fieldUpdater);
setter.set(listener1);
assertSame(listener1, channel.getListener());
setter.set(listener2);
assertSame(listener2, channel.getListener());
setter.set(listener3);
assertSame(listener3, channel.getListener());
setter.set(null);
assertSame(null, channel.getListener());
}
@Test
public void channelListenerSetterByAtomicReference() {
final AtomicReference<ChannelListener<? super ConnectedStreamChannelMock>> atomicReference = new AtomicReference<ChannelListener<? super ConnectedStreamChannelMock>>();
final TestChannelListener<ConnectedStreamChannelMock> listener1 = new TestChannelListener<ConnectedStreamChannelMock>();
final TestChannelListener<ConnectedStreamChannelMock> listener2 = new TestChannelListener<ConnectedStreamChannelMock>();
final TestChannelListener<ConnectedStreamChannelMock> listener3 = new TestChannelListener<ConnectedStreamChannelMock>();
final ChannelListener.Setter<ConnectedStreamChannelMock> setter = ChannelListeners.
<ConnectedStreamChannelMock>getSetter(atomicReference);
setter.set(listener1);
assertSame(listener1, atomicReference.get());
setter.set(listener2);
assertSame(listener2, atomicReference.get());
setter.set(listener3);
assertSame(listener3, atomicReference.get());
setter.set(null);
assertSame(null, atomicReference.get());
}
@Test
public void channelListenerDelegatingSetter() {
final AtomicReference<ChannelListener<? super ConnectedStreamChannelMock>> atomicReference = new AtomicReference<ChannelListener<? super ConnectedStreamChannelMock>>();
final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock();
final TestChannelListener<ConnectedStreamChannelMock> listener1 = new TestChannelListener<ConnectedStreamChannelMock>();
final TestChannelListener<ConnectedStreamChannelMock> listener2 = new TestChannelListener<ConnectedStreamChannelMock>();
final TestChannelListener<ConnectedStreamChannelMock> listener3 = new TestChannelListener<ConnectedStreamChannelMock>();
final ChannelListener.Setter<ConnectedStreamChannelMock> setter = ChannelListeners.
<ConnectedStreamChannelMock>getSetter(atomicReference);
final ChannelListener.Setter<ConnectedStreamChannelMock> delegatingSetter = ChannelListeners.getDelegatingSetter(setter, channelMock);
delegatingSetter.set(listener1);
final ChannelListener<? super ConnectedStreamChannelMock> delegatingListener1 = atomicReference.get();
assertNotNull(delegatingListener1);
delegatingSetter.set(listener2);
final ChannelListener<? super ConnectedStreamChannelMock> delegatingListener2 = atomicReference.get();
assertNotNull(delegatingListener2);
delegatingSetter.set(listener3);
final ChannelListener<? super ConnectedStreamChannelMock> delegatingListener3 = atomicReference.get();
assertNotNull(delegatingListener2);
delegatingSetter.set(null);
assertSame(null, atomicReference.get());
assertFalse(listener1.isInvoked());
delegatingListener1.handleEvent(null);
assertTrue(listener1.isInvoked());
assertSame(channelMock, listener1.getTargetChannel());
assertFalse(listener2.isInvoked());
delegatingListener2.handleEvent(new ConnectedStreamChannelMock());
assertTrue(listener2.isInvoked());
assertSame(channelMock, listener2.getTargetChannel());
assertFalse(listener3.isInvoked());
delegatingListener3.handleEvent(channelMock);
assertTrue(listener3.isInvoked());
assertSame(channelMock, listener3.getTargetChannel());
assertNull(ChannelListeners.getDelegatingSetter(null, channelMock));
}
@Test
public void nullSetter() {
final ChannelListener.Setter<ConnectedStreamChannelMock> nullSetter = ChannelListeners.<ConnectedStreamChannelMock>nullSetter();
assertNotNull(nullSetter);
// nothing should happen
nullSetter.set(null);
nullSetter.set(new TestChannelListener<ConnectedStreamChannelMock>());
}
@Test
public void executorChanneListener() {
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
final DeterministicExecutor executor = new DeterministicExecutor();
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
final ChannelListener<ConnectedStreamChannelMock> executorListener = ChannelListeners.executorChannelListener(listener, executor);
assertNotNull(executorListener);
executorListener.handleEvent(channel);
assertFalse(listener.isInvoked());
executor.runPendingCommands();
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
final Executor failingExecutor = new ExecutionRejector();
final ChannelListener<ConnectedStreamChannelMock> failingExecutorListener = ChannelListeners.executorChannelListener(listener, failingExecutor);
assertNotNull(failingExecutor);
listener.clear();
assertTrue(channel.isOpen());
failingExecutorListener.handleEvent(channel);
assertFalse(listener.isInvoked());
assertFalse(channel.isOpen());
}
@Test
public void flushingChannelListener() throws IOException {
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
final FailingChannel channel = new FailingChannel();
final TestExceptionHandler<ConnectedStreamChannelMock> exceptionHandler = new TestExceptionHandler<ConnectedStreamChannelMock>();
final ChannelListener<ConnectedStreamChannelMock> flushingListener = ChannelListeners.
flushingChannelListener(listener, exceptionHandler);
assertNotNull(flushingListener);
assertNotNull(flushingListener.toString());
final ByteBuffer tempBuffer = ByteBuffer.allocate(10);
tempBuffer.put("anything".getBytes()).flip();
channel.write(tempBuffer);
assertFalse(channel.isFlushed());
channel.enableFlush(false);
// try to flush, but flush will return false
assertFalse(channel.isWriteResumed());
assertNull(channel.getWriteListener());
flushingListener.handleEvent(channel);
assertSame(flushingListener, channel.getWriteListener());
assertTrue(channel.isWriteResumed());
assertFalse(channel.isFlushed());
// try to flush again, this time flush will throw an IOException
final IOException flushFailure = new IOException("Test exception");
channel.throwExceptionOnFlush(flushFailure);
assertFalse(exceptionHandler.isInvoked());
flushingListener.handleEvent(channel);
assertFalse(channel.isWriteResumed());
assertTrue(exceptionHandler.isInvoked());
assertSame(channel, exceptionHandler.getFailingChannel());
assertSame(flushFailure, exceptionHandler.getFailure());
// try to flush again, this time flush will return true
channel.enableFlush(true);
flushingListener.handleEvent(channel);
assertSame(listener, channel.getWriteListener());
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void writeShutdownListener() {
final FailingChannel channel = new FailingChannel();
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
final TestExceptionHandler<ConnectedStreamChannelMock> exceptionHandler = new TestExceptionHandler<ConnectedStreamChannelMock>();
final ChannelListener<ConnectedStreamChannelMock> writeShutdownListener = ChannelListeners.writeShutdownChannelListener(listener, exceptionHandler);
assertNotNull(writeShutdownListener);
// try to handle event, shutdownWrites will throw an exception
final IOException exception = new IOException("Test exception");
channel.throwExceptionOnShutdownWrites(exception);
writeShutdownListener.handleEvent(channel);
assertFalse(listener.isInvoked());
assertTrue(exceptionHandler.isInvoked());
assertSame(channel, exceptionHandler.getFailingChannel());
assertSame(exception, exceptionHandler.getFailure());
// try again, this time no exception will be thrown
assertFalse(channel.isShutdownWrites());
writeShutdownListener.handleEvent(channel);
assertTrue(channel.isShutdownWrites());
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void writingChannelListener() {
final Pool<ByteBuffer> pool = new ByteBufferSlicePool(15, 15);
final Pooled<ByteBuffer> pooledBuffer1 = pool.allocate();
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
final FailingChannel channel = new FailingChannel();
final TestExceptionHandler<ConnectedStreamChannelMock> exceptionHandler = new TestExceptionHandler<ConnectedStreamChannelMock>();
final ChannelListener<ConnectedStreamChannelMock> writingListener1 = ChannelListeners.writingChannelListener(pooledBuffer1, listener, exceptionHandler);
assertNotNull(writingListener1);
assertNotNull(writingListener1.toString());
// attempt to write will fail
final IOException writeException = new IOException("Test exception");
channel.throwExceptionOnWrite(writeException);
channel.resumeWrites();
writingListener1.handleEvent(channel);
assertFalse(channel.isWriteResumed());
assertTrue(exceptionHandler.isInvoked());
assertSame(channel, exceptionHandler.getFailingChannel());
assertSame(writeException, exceptionHandler.getFailure());
IllegalStateException expected = null;
try {
pooledBuffer1.getResource();
} catch (IllegalStateException e) {
expected = e;
}
assertNotNull(expected);
final Pooled<ByteBuffer> pooledBuffer2 = pool.allocate();
pooledBuffer2.getResource().put("abc".getBytes()).flip();
final ChannelListener<ConnectedStreamChannelMock> writingListener2 = ChannelListeners.writingChannelListener(pooledBuffer2, listener, exceptionHandler);
assertNotNull(writingListener2);
assertNotNull(writingListener2.toString());
// attempt again to write... this time channel will refuse to perform write
channel.enableWrite(false);
writingListener2.handleEvent(channel);
assertSame(writingListener2, channel.getWriteListener());
assertTrue(channel.isWriteResumed());
assertFalse(listener.isInvoked());
// attempt again to write... this time channel will perform the write
channel.getWriteSetter().set(null);
channel.suspendWrites();
channel.enableWrite(true);
assertWrittenMessage(channel);
writingListener2.handleEvent(channel);
assertWrittenMessage(channel, "abc");
assertNull(channel.getWriteListener());
assertFalse(channel.isWriteResumed());
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
final Pooled<ByteBuffer> pooledBuffer3 = pool.allocate();
pooledBuffer3.getResource().put("defghij".getBytes()).flip();
listener.clear();
final ChannelListener<ConnectedStreamChannelMock> writingListener3 = ChannelListeners.writingChannelListener(pooledBuffer3, listener, exceptionHandler);
assertNotNull(writingListener3);
assertNotNull(writingListener3.toString());
// attempt again to write... this time channel will perform a write of part of the bytes and listener
// will have to make several requests to write, in order to write the 7 bytes
channel.limitWrite(3);
assertWrittenMessage(channel, "abc");
writingListener3.handleEvent(channel);
assertWrittenMessage(channel, "abc", "defghij");
assertNull(channel.getWriteListener());
assertFalse(channel.isWriteResumed());
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void sendingChannelListener() {
final Pool<ByteBuffer> pool = new ByteBufferSlicePool(15, 15);
final Pooled<ByteBuffer> pooledBuffer1 = pool.allocate();
final TestChannelListener<MessageChannelMock> listener = new TestChannelListener<MessageChannelMock>();
final FailingChannel connectedChannel = new FailingChannel();
final MessageChannelMock channel = new MessageChannelMock(connectedChannel);
final TestExceptionHandler<MessageChannelMock> exceptionHandler = new TestExceptionHandler<MessageChannelMock>();
final ChannelListener<MessageChannelMock> sendingListener1 = ChannelListeners.
<MessageChannelMock>sendingChannelListener(pooledBuffer1, listener, exceptionHandler);
assertNotNull(sendingListener1);
assertNotNull(sendingListener1.toString());
// attempt to write will fail
final IOException writeException = new IOException("Test exception");
connectedChannel.throwExceptionOnWrite(writeException);
connectedChannel.resumeWrites();
channel.resumeWrites();
sendingListener1.handleEvent(channel);
assertFalse(channel.isWriteResumed());
assertTrue(exceptionHandler.isInvoked());
assertSame(channel, exceptionHandler.getFailingChannel());
assertSame(writeException, exceptionHandler.getFailure());
IllegalStateException expected = null;
try {
pooledBuffer1.getResource();
} catch (IllegalStateException e) {
expected = e;
}
assertNotNull(expected);
final Pooled<ByteBuffer> pooledBuffer2 = pool.allocate();
final ChannelListener<MessageChannelMock> sendingListener2 = ChannelListeners.
<MessageChannelMock>sendingChannelListener(pooledBuffer2, listener, exceptionHandler);
assertNotNull(sendingListener2);
assertNotNull(sendingListener2.toString());
// attempt to write will fail again, this time with a runtime exception
final RuntimeException runtimeWriteException = new RuntimeException("Test exception");
connectedChannel.throwExceptionOnWrite(runtimeWriteException);
connectedChannel.resumeWrites();
channel.resumeWrites();
exceptionHandler.clear();
RuntimeException expectedWriteException = null;
try {
sendingListener2.handleEvent(channel);
} catch (RuntimeException e) {
expectedWriteException = e;
}
assertSame(runtimeWriteException, expectedWriteException);
assertTrue(channel.isWriteResumed());
assertFalse(exceptionHandler.isInvoked());
expected = null;
try {
pooledBuffer2.getResource();
} catch (IllegalStateException e) {
expected = e;
}
assertNotNull(expected);
final Pooled<ByteBuffer> pooledBuffer3 = pool.allocate();
pooledBuffer3.getResource().put("abc".getBytes()).flip();
final ChannelListener<MessageChannelMock> sendingListener3 = ChannelListeners.
<MessageChannelMock>sendingChannelListener(pooledBuffer3, listener, exceptionHandler);
assertNotNull(sendingListener3);
assertNotNull(sendingListener3.toString());
// attempt again to write... this time channel will refuse to perform write
connectedChannel.enableWrite(false);
sendingListener3.handleEvent(channel);
assertSame(sendingListener3, channel.getWriteListener());
assertTrue(channel.isWriteResumed());
assertFalse(listener.isInvoked());
// attempt again to write... this time channel will perform the write
channel.getWriteSetter().set(null);
channel.suspendWrites();
connectedChannel.enableWrite(true);
assertWrittenMessage(connectedChannel);
sendingListener3.handleEvent(channel);
assertWrittenMessage(connectedChannel, "abc");
assertNull(channel.getWriteListener());
assertFalse(channel.isWriteResumed());
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void fileSendingChannelListener() throws IOException {
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
final TestExceptionHandler<ConnectedStreamChannelMock> exceptionHandler = new TestExceptionHandler<ConnectedStreamChannelMock>();
final FailingChannel channel = new FailingChannel();
final File file = File.createTempFile("test", ".txt");
file.deleteOnExit();
final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
final FileChannel fileChannel = randomAccessFile.getChannel();
try {
final ByteBuffer buffer = ByteBuffer.allocate(10);
buffer.put("test".getBytes("UTF-8")).flip();
assertEquals(4, fileChannel.write(buffer));
fileChannel.position(0);
final ChannelListener<ConnectedStreamChannelMock> fileSendingChannelListener1 = ChannelListeners.
<ConnectedStreamChannelMock>fileSendingChannelListener(fileChannel, 0, 4, listener, exceptionHandler);
// attempt to transfer, it will fail because writing is disabled on the channel
channel.enableWrite(false);
assertNull(channel.getWriteListener());
assertFalse(channel.isWriteResumed());
fileSendingChannelListener1.handleEvent(channel);
assertSame(fileSendingChannelListener1, channel.getWriteListener());
assertTrue(channel.isWriteResumed());
// attempt again, this time with write enabled
channel.enableWrite(true);
assertFalse(listener.isInvoked());
assertTrue(channel.getWrittenText().isEmpty());
fileSendingChannelListener1.handleEvent(channel);
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
assertWrittenMessage(channel, "test");
listener.clear();
final ChannelListener<ConnectedStreamChannelMock> fileSendingChannelListener2 = ChannelListeners.
<ConnectedStreamChannelMock>fileSendingChannelListener(fileChannel, 0, 0, listener, exceptionHandler);
fileSendingChannelListener2.handleEvent(channel);
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
final ChannelListener<ConnectedStreamChannelMock> fileSendingChannelListener3 = ChannelListeners.
<ConnectedStreamChannelMock>fileSendingChannelListener(fileChannel, 0, 10, listener, exceptionHandler);
buffer.clear();
buffer.put("1234567890".getBytes()).flip();
assertEquals(10, fileChannel.write(buffer));
final IOException writeFailure = new IOException("Test exception");
channel.throwExceptionOnWrite(writeFailure);
assertFalse(exceptionHandler.isInvoked());
fileSendingChannelListener3.handleEvent(channel);
assertTrue(exceptionHandler.isInvoked());
assertSame(channel, exceptionHandler.getFailingChannel());
assertSame(writeFailure, exceptionHandler.getFailure());
channel.limitWrite(3);
listener.clear();
assertWrittenMessage(channel, "test");
fileSendingChannelListener3.handleEvent(channel);
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
assertWrittenMessage(channel, "test", "1234567890");
} finally {
randomAccessFile.close();
fileChannel.close();
}
}
@Test
public void fileReceivingChannelListener() throws IOException {
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
final TestExceptionHandler<ConnectedStreamChannelMock> exceptionHandler = new TestExceptionHandler<ConnectedStreamChannelMock>();
final FailingChannel channel = new FailingChannel();
channel.setReadData("test");
final File file = File.createTempFile("test", ".txt");
file.deleteOnExit();
final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
final FileChannelWrapper fileChannel = new FileChannelWrapper(randomAccessFile.getChannel());
try {
final ChannelListener<ConnectedStreamChannelMock> fileReceivingChannelListener1 = ChannelListeners.
<ConnectedStreamChannelMock>fileReceivingChannelListener(fileChannel, 0, 4, listener, exceptionHandler);
// attempt to transfer, it will fail because reading is disabled on the channel
channel.enableRead(false);
assertNull(channel.getReadListener());
assertFalse(channel.isReadResumed());
fileReceivingChannelListener1.handleEvent(channel);
assertSame(fileReceivingChannelListener1, channel.getReadListener());
assertTrue(channel.isReadResumed());
// attempt again, this time with read enabled
channel.enableRead(true);
assertFalse(listener.isInvoked());
final ByteBuffer buffer = ByteBuffer.allocate(15);
fileChannel.read(buffer);
assertReadMessage(buffer);
fileReceivingChannelListener1.handleEvent(channel);
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
buffer.clear();
fileChannel.read(buffer);
assertReadMessage(buffer, "test");
listener.clear();
final ChannelListener<ConnectedStreamChannelMock> fileReceivingChannelListener2 = ChannelListeners.
<ConnectedStreamChannelMock>fileReceivingChannelListener(fileChannel, 0, 0, listener, exceptionHandler);
fileReceivingChannelListener2.handleEvent(channel);
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
final ChannelListener<ConnectedStreamChannelMock> fileReceivingChannelListener3 = ChannelListeners.
<ConnectedStreamChannelMock>fileReceivingChannelListener(fileChannel, 4, 10, listener, exceptionHandler);
channel.setReadData("1234567890");
final IOException readFailure = new IOException("Test exception");
channel.throwExceptionOnRead(readFailure);
assertFalse(exceptionHandler.isInvoked());
fileReceivingChannelListener3.handleEvent(channel);
assertTrue(exceptionHandler.isInvoked());
assertSame(channel, exceptionHandler.getFailingChannel());
assertSame(readFailure, exceptionHandler.getFailure());
fileChannel.limitTransfer(3);
listener.clear();
buffer.clear();
fileChannel.position(0);
fileChannel.read(buffer);
assertReadMessage(buffer, "test");
fileReceivingChannelListener3.handleEvent(channel);
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
buffer.clear();
fileChannel.position(0);
fileChannel.read(buffer);
assertReadMessage(buffer, "test", "1234567890");
} finally {
randomAccessFile.close();
fileChannel.close();
}
}
@Test
public void delegatingChannelListener() {
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
final ChannelListener<ConnectedStreamChannelMock> delegatingListener = ChannelListeners.
<ConnectedStreamChannelMock>delegatingChannelListener(listener);
assertNotNull(delegatingListener);
assertFalse(listener.isInvoked());
delegatingListener.handleEvent(channel);
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void writeSuspendingChannelListener() {
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
final ChannelListener<ConnectedStreamChannelMock> writeSuspendingListener = ChannelListeners.
<ConnectedStreamChannelMock>writeSuspendingChannelListener(listener);
assertNotNull(writeSuspendingListener);
assertFalse(listener.isInvoked());
channel.resumeWrites();
writeSuspendingListener.handleEvent(channel);
assertFalse(channel.isWriteResumed());
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
@Test
public void readSuspendingChannelListener() {
final TestChannelListener<ConnectedStreamChannelMock> listener = new TestChannelListener<ConnectedStreamChannelMock>();
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
final ChannelListener<ConnectedStreamChannelMock> readSuspendingListener = ChannelListeners.
<ConnectedStreamChannelMock>readSuspendingChannelListener(listener);
assertNotNull(readSuspendingListener);
assertFalse(listener.isInvoked());
channel.resumeReads();
readSuspendingListener.handleEvent(channel);
assertFalse(channel.isReadResumed());
assertTrue(listener.isInvoked());
assertSame(channel, listener.getTargetChannel());
}
private static class TestChannelListener<C extends Channel> implements ChannelListener<C> {
private boolean invoked = false;
private Channel channel = null;
private boolean throwException = false;
public void throwExceptionOnHandle() {
throwException = true;
}
@Override
public void handleEvent(C c) {
invoked = true;
channel = c;
if (throwException) {
throw new RuntimeException("Test exception");
}
}
public boolean isInvoked() {
return invoked;
}
public Channel getTargetChannel() {
return channel;
}
public void clear() {
invoked = false;
channel = null;
}
}
private static class ExecutionRejector implements Executor {
@Override
public void execute(Runnable command) {
throw new RejectedExecutionException("Test execption");
}
}
private static class ListenerSetterTesterChannel extends ConnectedStreamChannelMock {
private volatile ChannelListener<ConnectedStreamChannelMock> listener = null;
@SuppressWarnings("rawtypes")
public AtomicReferenceFieldUpdater<ListenerSetterTesterChannel, ChannelListener> getFieldUpdater() {
return AtomicReferenceFieldUpdater.newUpdater(ListenerSetterTesterChannel.class, ChannelListener.class, "listener");
}
public ChannelListener<ConnectedStreamChannelMock> getListener() {
return listener;
}
}
private static class FailingChannel extends ConnectedStreamChannelMock {
private IOException flushException = null;
private IOException writeShutdownException = null;
private IOException writeException = null;
private RuntimeException runtimeWriteException = null;
private IOException readException = null;
private int writeLimit = -1;
public void throwExceptionOnFlush(IOException exception) {
flushException = exception;
}
@Override
public boolean flush() throws IOException {
if (flushException != null) {
try {
throw flushException;
} finally {
flushException = null;
}
}
return super.flush();
}
public void throwExceptionOnShutdownWrites(IOException exception) {
writeShutdownException = exception;
}
@Override
public void shutdownWrites() throws IOException {
if (writeShutdownException != null) {
try {
throw writeShutdownException;
} finally {
writeShutdownException = null;
}
}
super.shutdownWrites();
}
public void throwExceptionOnWrite(IOException exception) {
writeException = exception;
}
public void throwExceptionOnWrite(RuntimeException exception) {
runtimeWriteException = exception;
}
@Override
public synchronized int write(ByteBuffer src) throws IOException {
if (writeException != null) {
try {
throw writeException;
} finally {
writeException = null;
}
}
if (runtimeWriteException != null) {
try {
throw runtimeWriteException;
} finally {
runtimeWriteException = null;
}
}
if (writeLimit == -1) {
return super.write(src);
}
int originalLimit = src.limit();
if (src.remaining() > writeLimit) {
src.limit(originalLimit - src.remaining() + writeLimit);
}
try {
return super.write(src);
} finally {
src.limit(originalLimit);
}
}
@Override
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
if (writeException != null) {
try {
throw writeException;
} finally {
writeException = null;
}
}
if (runtimeWriteException != null) {
try {
throw runtimeWriteException;
} finally {
runtimeWriteException = null;
}
}
if (writeLimit == -1) {
return super.write(srcs, offset, length);
}
int extraByteLength = (int) Buffers.remaining(srcs, offset, length) - writeLimit;
int originalLimit = -1;
if (extraByteLength > 0) {
for (int i = offset + length - 1; extraByteLength > 0; i--) {
if (srcs[i].remaining() > extraByteLength) {
extraByteLength -= srcs[i].remaining();
} else {
length = i - offset + 1;
originalLimit = srcs[i].limit();
srcs[i].limit(originalLimit - extraByteLength);
extraByteLength = 0;
}
}
}
try {
return super.write(srcs, offset, length);
} finally {
if (originalLimit != -1) {
srcs[length + offset - 1].limit(originalLimit);
}
}
}
@Override
public long write(ByteBuffer[] srcs) throws IOException {
if (writeException != null) {
try {
throw writeException;
} finally {
writeException = null;
}
}
if (runtimeWriteException != null) {
try {
throw runtimeWriteException;
} finally {
runtimeWriteException = null;
}
}
if (writeLimit == -1) {
return super.write(srcs);
}
int extraByteLength = (int) Buffers.remaining(srcs) - writeLimit;
int originalLimit = -1;
int length = srcs.length;
if (extraByteLength > 0) {
for (int i = srcs.length - 1; extraByteLength > 0; i--) {
if (srcs[i].remaining() > extraByteLength) {
extraByteLength -= srcs[i].remaining();
} else {
length = i + 1;
originalLimit = srcs[i].limit();
srcs[i].limit(originalLimit - extraByteLength);
extraByteLength = 0;
}
}
}
try {
return super.write(srcs, 0, length);
} finally {
if (originalLimit != -1) {
srcs[length - 1].limit(originalLimit);
}
}
}
public void limitWrite(int limit) {
writeLimit = limit;
}
public void throwExceptionOnRead(IOException exception) {
readException = exception;
}
@Override
public synchronized int read(ByteBuffer dst) throws IOException {
if (readException != null) {
try {
throw readException;
} finally {
readException = null;
}
}
return super.read(dst);
}
@Override
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
if (readException != null) {
try {
throw readException;
} finally {
readException = null;
}
}
return super.read(dsts, offset, length);
}
@Override
public long read(ByteBuffer[] dsts) throws IOException {
if (readException != null) {
try {
throw readException;
} finally {
readException = null;
}
}
return super.read(dsts);
}
}
private static class TestExceptionHandler<C extends Channel> implements ChannelExceptionHandler<C> {
private boolean invoked = false;
private C channel;
private IOException exception;
@Override
public void handleException(C c, IOException e) {
invoked = true;
channel = c;
exception = e;
}
public boolean isInvoked() {
return invoked;
}
public C getFailingChannel() {
return channel;
}
public IOException getFailure() {
return exception;
}
public void clear() {
invoked = false;
channel = null;
exception = null;
}
}
private static class FileChannelWrapper extends FileChannel {
private final FileChannel delegate;
private int transferLimit = -1;
public FileChannelWrapper(FileChannel d) {
delegate = d;
}
public void limitTransfer(int limit) {
transferLimit = limit;
}
@Override
public int read(ByteBuffer dst) throws IOException {
return delegate.read(dst);
}
@Override
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
return delegate.read(dsts, offset, length);
}
@Override
public int write(ByteBuffer src) throws IOException {
return delegate.write(src);
}
@Override
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
return delegate.write(srcs, offset, length);
}
@Override
public long position() throws IOException {
return delegate.position();
}
@Override
public FileChannel position(long newPosition) throws IOException {
return delegate.position(newPosition);
}
@Override
public long size() throws IOException {
return delegate.size();
}
@Override
public FileChannel truncate(long size) throws IOException {
return delegate.truncate(size);
}
@Override
public void force(boolean metaData) throws IOException {
delegate.force(metaData);
}
@Override
public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
if (transferLimit != -1 && count > transferLimit) {
count = transferLimit;
}
return delegate.transferTo(position, count, target);
}
@Override
public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
if (transferLimit != -1 && count > transferLimit) {
count = transferLimit;
}
return delegate.transferFrom(src, position, count);
}
@Override
public int read(ByteBuffer dst, long position) throws IOException {
return delegate.read(dst, position);
}
@Override
public int write(ByteBuffer src, long position) throws IOException {
return delegate.write(src, position);
}
@Override
public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
return delegate.map(mode, position, size);
}
@Override
public FileLock lock(long position, long size, boolean shared) throws IOException {
return delegate.lock(position, size, shared);
}
@Override
public FileLock tryLock(long position, long size, boolean shared) throws IOException {
return delegate.tryLock(position, size, shared);
}
@Override
protected void implCloseChannel() throws IOException {}
}
}