/*
* JBoss, Home of Professional Open Source
*
* Copyright 2012 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.xnio.AssertReadWrite.assertWrittenMessage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.Channels;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.spi.SelectorProvider;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import junit.framework.TestCase;
import org.xnio.IoFuture.Status;
import org.xnio.channels.ConnectedStreamChannel;
import org.xnio.mock.ConnectedStreamChannelMock;
/**
* Test for {@link IoUtils}.
*
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
* @author <a href="mailto:flavia.rainone@jboss.com">Flavia Rainone</a>
*
*/
public final class IoUtilsTestCase extends TestCase {
public void testDirectExecutor() {
final Thread t = Thread.currentThread();
final boolean ok[] = new boolean[1];
IoUtils.directExecutor().execute(new Runnable() {
public void run() {
assertSame(t, Thread.currentThread());
ok[0] = true;
}
});
assertTrue(ok[0]);
assertNotNull(IoUtils.directExecutor().toString());
}
public void testNullExecutor() {
IoUtils.nullExecutor().execute(new Runnable() {
public void run() {
fail("null executor ran task");
}
});
assertNotNull(IoUtils.nullExecutor().toString());
}
public void testNullCloseable() throws IOException {
//nothing should happen
IoUtils.nullCloseable().close();
IoUtils.nullCloseable().close();
IoUtils.nullCloseable().close();
IoUtils.nullCloseable().close();
IoUtils.nullCloseable().close();
assertNotNull(IoUtils.nullCloseable().toString());
}
public void testNullCancellable() throws IOException {
//nothing should happen
assertSame(IoUtils.nullCancellable(), IoUtils.nullCancellable().cancel());
assertSame(IoUtils.nullCancellable(), IoUtils.nullCancellable().cancel());
assertSame(IoUtils.nullCancellable(), IoUtils.nullCancellable().cancel());
assertSame(IoUtils.nullCancellable(), IoUtils.nullCancellable().cancel());
assertNotNull(IoUtils.nullCancellable().toString());
}
public void testSafeClose() {
IoUtils.safeClose(new Closeable() {
public void close() throws IOException {
throw new RuntimeException("This error should be consumed but logged");
}
});
IoUtils.safeClose(new Closeable() {
public void close() throws IOException {
throw new Error("This error should be consumed but logged");
}
});
IoUtils.safeClose(new Closeable() {
public void close() throws IOException {
throw new IOException("This error should be consumed but logged");
}
});
IoUtils.safeClose(new Closeable() {
public void close() throws IOException {
throw new ClosedChannelException(); // this error should be ignored
}
});
IoUtils.safeClose((Closeable) null); // should do nothing if target is null
}
public void testSafeCloseSocket() {
IoUtils.safeClose(new Socket() {
public void close() throws IOException {
throw new RuntimeException("This error should be consumed but logged");
}
});
IoUtils.safeClose(new Socket() {
public void close() throws IOException {
throw new Error("This error should be consumed but logged");
}
});
IoUtils.safeClose(new Socket() {
public void close() throws IOException {
throw new IOException("This error should be consumed but logged");
}
});
IoUtils.safeClose(new Socket() {
public void close() throws IOException {
throw new ClosedChannelException(); // this error should be ignored
}
});
IoUtils.safeClose((Socket) null); // should do nothing if target is null
}
public void testSafeCloseDatagramSocket() throws SocketException {
IoUtils.safeClose(new DatagramSocket() {
public void close() {
throw new RuntimeException("This error should be consumed but logged");
}
});
IoUtils.safeClose(new DatagramSocket() {
public void close() {
throw new Error("This error should be consumed but logged");
}
});
IoUtils.safeClose((DatagramSocket) null); // should do nothing if target is null
}
public void testSafeCloseSelector() {
IoUtils.safeClose(new TestSelector() {
public void close() throws IOException {
throw new RuntimeException("This error should be consumed but logged");
}
});
IoUtils.safeClose(new TestSelector() {
public void close() throws IOException {
throw new Error("This error should be consumed but logged");
}
});
IoUtils.safeClose(new TestSelector() {
public void close() throws IOException {
throw new IOException("This error should be consumed but logged");
}
});
IoUtils.safeClose(new TestSelector() {
public void close() throws IOException {
throw new ClosedChannelException(); // this error should be ignored
}
});
IoUtils.safeClose((Selector) null); // should do nothing if target is null
}
public void testSafeCloseServerSocket() throws IOException {
IoUtils.safeClose(new ServerSocket() {
public void close() throws IOException {
throw new RuntimeException("This error should be consumed but logged");
}
});
IoUtils.safeClose(new ServerSocket() {
public void close() throws IOException {
throw new Error("This error should be consumed but logged");
}
});
IoUtils.safeClose(new ServerSocket() {
public void close() throws IOException {
throw new IOException("This error should be consumed but logged");
}
});
IoUtils.safeClose(new ServerSocket() {
public void close() throws IOException {
throw new ClosedChannelException(); // this error should be ignored
}
});
IoUtils.safeClose((ServerSocket) null); // should do nothing if target is null
}
public void testSafeCloseZipFile() throws IOException {
final File file = File.createTempFile("foo", ".zip");
file.deleteOnExit();
final ZipOutputStream zipOutput = new ZipOutputStream(new FileOutputStream(file));
zipOutput.close();
IoUtils.safeClose(new ZipFile(file) {
public void close() throws IOException {
throw new RuntimeException("This error should be consumed but logged");
}
});
IoUtils.safeClose(new ZipFile(file) {
public void close() throws IOException {
throw new Error("This error should be consumed but logged");
}
});
IoUtils.safeClose(new ZipFile(file) {
public void close() throws IOException {
throw new IOException("This error should be consumed but logged");
}
});
IoUtils.safeClose((ZipFile) null); // should do nothing if target is null
}
public void testSafeCloseHandler() throws IOException {
IoUtils.safeClose(new Handler() {
public void close() {
throw new RuntimeException("This error should be consumed but logged");
}
@Override public void publish(LogRecord record) {}
@Override public void flush() {}
});
IoUtils.safeClose(new Handler() {
public void close() {
throw new Error("This error should be consumed but logged");
}
@Override public void publish(LogRecord record) {}
@Override public void flush() {}
});
IoUtils.safeClose(new Handler() {
public void close() throws SecurityException {
throw new SecurityException("This error should be consumed but logged");
}
@Override public void publish(LogRecord record) {}
@Override public void flush() {}
});
IoUtils.safeClose((Handler) null); // should do nothing if target is null
}
public void testSafeCloseIoFuture() {
final TestIoFuture<Closeable> future1 = new TestIoFuture<Closeable>();
IoUtils.safeClose(future1);
assertEquals(IoFuture.Status.CANCELLED, future1.getStatus());
final TestIoFuture<Closeable> future2 = new TestIoFuture<Closeable>();
final TestCloseable closeable = new TestCloseable();
assertTrue(future2.setResult(closeable));
assertTrue(closeable.isOpen());
IoUtils.safeClose(future2);
assertEquals(IoFuture.Status.DONE, future2.getStatus());
assertFalse(closeable.isOpen());
// should do nothing if target is null
IoUtils.safeClose((IoFuture<? extends Closeable>) null);
}
public void testAttachmentClosingNotifier() {
final TestCloseable closeable = new TestCloseable();
assertTrue(closeable.isOpen());
IoUtils.attachmentClosingNotifier().notify(null, closeable);
assertFalse(closeable.isOpen());
}
public void testClosingNotifier() {
final TestCloseable closeable = new TestCloseable();
final TestIoFuture<Closeable> future = new TestIoFuture<Closeable>();
future.addNotifier(IoUtils.closingNotifier(), null);
assertTrue(closeable.isOpen());
future.setResult(closeable);
assertFalse(closeable.isOpen());
}
public void testRunnableNotifier() {
final TestRunnable testRunnable = new TestRunnable();
final IoFuture.Notifier<Void, Void> notifier = IoUtils.runnableNotifier(testRunnable);
assertFalse(testRunnable.isInvoked());
notifier.notify(null, null);
assertTrue(testRunnable.isInvoked());
}
public void testResultNotifier() throws Exception {
final FutureResult<Closeable> futureResult1 = new FutureResult<Closeable>();
final FutureResult<Closeable> futureResult2 = new FutureResult<Closeable>();
final FutureResult<Closeable> futureResult3 = new FutureResult<Closeable>();
final TestIoFuture<Closeable> future1 = new TestIoFuture<Closeable>();
final TestIoFuture<Closeable> future2 = new TestIoFuture<Closeable>();
final TestIoFuture<Closeable> future3 = new TestIoFuture<Closeable>();
future1.addNotifier(IoUtils.<Closeable>resultNotifier(), futureResult1);
future2.addNotifier(IoUtils.<Closeable>resultNotifier(), futureResult2);
future3.addNotifier(IoUtils.<Closeable>resultNotifier(), futureResult3);
future1.cancel();
assertSame(Status.CANCELLED, futureResult1.getIoFuture().getStatus());
final IOException exception = new IOException("Test exception");
future2.setException(exception);
assertSame(Status.FAILED, futureResult2.getIoFuture().getStatus());
assertSame(exception, futureResult2.getIoFuture().getException());
final Closeable result = new TestCloseable();
future3.setResult(result);
assertSame(Status.DONE, futureResult3.getIoFuture().getStatus());
assertSame(result, futureResult3.getIoFuture().get());
}
public void testChannelListenerNotifier() {
final ConnectedStreamChannelMock channel = new ConnectedStreamChannelMock();
final TestChannelListener channelListener = new TestChannelListener();
final TestIoFuture<ConnectedStreamChannel> future1 = new TestIoFuture<ConnectedStreamChannel>();
future1.<ChannelListener<? super ConnectedStreamChannel>>addNotifier(
IoUtils.<ConnectedStreamChannel>channelListenerNotifier(), channelListener);
final TestIoFuture<ConnectedStreamChannel> future2 = new TestIoFuture<ConnectedStreamChannel>();
future2.<ChannelListener<? super ConnectedStreamChannel>>addNotifier(
IoUtils.<ConnectedStreamChannel>channelListenerNotifier(), channelListener);
final TestIoFuture<ConnectedStreamChannel> future3 = new TestIoFuture<ConnectedStreamChannel>();
future3.<ChannelListener<? super ConnectedStreamChannel>>addNotifier(
IoUtils.<ConnectedStreamChannel>channelListenerNotifier(), channelListener);
future1.cancel();
future2.setException(new IOException());
assertFalse(channelListener.isInvoked());
future3.setResult(channel);
assertTrue(channelListener.isInvoked());
assertSame(channel, channelListener.getChannel());
}
public void testIoFutureWrapper() throws Exception {
// test future wrapper with ioFuture1, which will have a value successfully set
final TestIoFuture<String> ioFuture1 = new TestIoFuture<String>();
final Future<String> future1 = IoUtils.getFuture(ioFuture1);
assertFalse(future1.isCancelled());
assertFalse(future1.isDone());
assertNotNull(future1.toString());
final FutureValueRetriever<String> futureValueRetriever1 = new FutureValueRetriever<String>(future1);
final FutureValueRetriever<String> futureValueRetriever2 = new FutureValueRetriever<String>(future1, 50, TimeUnit.MILLISECONDS);
final Thread retrieverThread1 = new Thread(futureValueRetriever1);
final Thread retrieverThread2 = new Thread(futureValueRetriever2);
retrieverThread1.start();
retrieverThread2.start();
retrieverThread1.join(80);
retrieverThread2.join(150);
assertTrue(retrieverThread1.isAlive());
assertFalse(retrieverThread2.isAlive());
assertNotNull(futureValueRetriever2.getTimeoutException());
ioFuture1.setResult("future1");
retrieverThread1.join();
assertSame("future1", futureValueRetriever1.getFutureValue());
assertFalse(future1.isCancelled());
assertTrue(future1.isDone());
assertFalse(future1.cancel(true));
// test future wrapper with ioFuture2, which will be canceled, by a call to ioFuture2.cancel()
final TestIoFuture<Integer> ioFuture2 = new TestIoFuture<Integer>();
final Future<Integer> future2 = IoUtils.getFuture(ioFuture2);
assertFalse(future2.isCancelled());
assertFalse(future2.isDone());
assertNotNull(future2.toString());
final FutureValueRetriever<Integer> futureValueRetriever3 = new FutureValueRetriever<Integer>(future2);
final FutureValueRetriever<Integer> futureValueRetriever4 = new FutureValueRetriever<Integer>(future2, 1, TimeUnit.DAYS);
final Thread retrieverThread3 = new Thread(futureValueRetriever3);
final Thread retrieverThread4 = new Thread(futureValueRetriever4);
retrieverThread3.start();
retrieverThread4.start();
retrieverThread3.join(60);
retrieverThread4.join(60);
assertTrue(retrieverThread3.isAlive());
assertTrue(retrieverThread4.isAlive());
ioFuture2.cancel();
retrieverThread3.join();
retrieverThread4.join();
assertNotNull(futureValueRetriever3.getCancellationException());
assertNotNull(futureValueRetriever4.getCancellationException());
assertTrue(future2.isCancelled());
assertFalse(future2.isDone());
assertTrue(future2.cancel(false));
// test future wrapper with ioFuture3, which will be canceled, by a call to the future wrapper
final TestIoFuture<Integer> ioFuture3 = new TestIoFuture<Integer>();
final Future<Integer> future3 = IoUtils.getFuture(ioFuture3);
assertFalse(future3.isCancelled());
assertFalse(future3.isDone());
assertNotNull(future3.toString());
final FutureValueRetriever<Integer> futureValueRetriever5 = new FutureValueRetriever<Integer>(future3);
final FutureValueRetriever<Integer> futureValueRetriever6 = new FutureValueRetriever<Integer>(future3, 10, TimeUnit.NANOSECONDS);
final Thread retrieverThread5 = new Thread(futureValueRetriever5);
final Thread retrieverThread6 = new Thread(futureValueRetriever6);
retrieverThread5.start();
retrieverThread6.start();
retrieverThread5.join(60);
retrieverThread6.join(60);
assertTrue(retrieverThread5.isAlive());
assertFalse(retrieverThread6.isAlive());
assertNotNull(futureValueRetriever6.getTimeoutException());
assertTrue(future3.cancel(true));
retrieverThread5.join();
assertNotNull(futureValueRetriever5.getCancellationException());
assertTrue(future3.isCancelled());
assertFalse(future3.isDone());
assertTrue(future3.cancel(true));
// test future wrapper with ioFuture3, which will be canceled, by a call to the future wrapper
final TestIoFuture<Void> ioFuture4 = new TestIoFuture<Void>();
final Future<Void> future4 = IoUtils.getFuture(ioFuture4);
assertFalse(future4.isCancelled());
assertFalse(future4.isDone());
assertNotNull(future4.toString());
final FutureValueRetriever<Void> futureValueRetriever7 = new FutureValueRetriever<Void>(future4);
final FutureValueRetriever<Void> futureValueRetriever8 = new FutureValueRetriever<Void>(future4, 1, TimeUnit.MINUTES);
final Thread retrieverThread7 = new Thread(futureValueRetriever7);
final Thread retrieverThread8 = new Thread(futureValueRetriever8);
retrieverThread7.start();
retrieverThread8.start();
retrieverThread7.join(60);
retrieverThread8.join(60);
assertTrue(retrieverThread7.isAlive());
assertTrue(retrieverThread8.isAlive());
final IOException failure = new IOException("Test exception");
ioFuture4.setException(failure);
retrieverThread7.join();
retrieverThread8.join();
assertSame(failure, futureValueRetriever7.getFailure());
assertSame(failure, futureValueRetriever8.getFailure());
assertFalse(future4.isCancelled());
assertFalse(future4.isDone());
assertFalse(future4.cancel(false));
}
public void testAwaitAll() throws InterruptedException {
final TestIoFuture<Void> future1 = new TestIoFuture<Void>();
final TestIoFuture<Void> future2 = new TestIoFuture<Void>();
final TestIoFuture<Void> future3 = new TestIoFuture<Void>();
final TestIoFuture<Void> future4 = new TestIoFuture<Void>();
final TestIoFuture<Void> future5 = new TestIoFuture<Void>();
final Awaiter awaiterTask1 = new Awaiter(future1, future2, future3, future4, future5);
final Awaiter awaiterTask2 = new Awaiter(future1, future3, future4);
final Awaiter awaiterTask3 = new Awaiter(future1, future2, future4, future5);
final Awaiter awaiterTask4 = new Awaiter(true, future1, future3, future4);
final Awaiter awaiterTask5 = new Awaiter();
final Thread awaiterThread1 = new Thread(awaiterTask1);
final Thread awaiterThread2 = new Thread(awaiterTask2);
final Thread awaiterThread3 = new Thread(awaiterTask3);
final Thread awaiterThread4 = new Thread(awaiterTask4);
final Thread awaiterThread5 = new Thread(awaiterTask5);
awaiterThread1.start();
awaiterThread2.start();
awaiterThread3.start();
awaiterThread4.start();
awaiterThread5.start();
awaiterThread1.join(100);
awaiterThread2.join(100);
awaiterThread3.join(100);
awaiterThread4.join(100);
awaiterThread5.join();
assertTrue(awaiterThread1.isAlive());
assertTrue(awaiterThread2.isAlive());
assertTrue(awaiterThread3.isAlive());
assertTrue(awaiterThread4.isAlive());
future2.setResult(null);
future3.cancel();
awaiterThread1.join(100);
awaiterThread2.join(100);
awaiterThread3.join(100);
awaiterThread4.join(100);
assertTrue(awaiterThread1.isAlive());
assertTrue(awaiterThread2.isAlive());
assertTrue(awaiterThread3.isAlive());
assertTrue(awaiterThread4.isAlive());
awaiterThread3.interrupt();
awaiterThread4.interrupt();
awaiterThread4.join();
assertNotNull(awaiterTask4.getInterruptedException());
future1.setResult(null);
future4.setException(new IOException());
awaiterThread1.join(100);
awaiterThread3.join(100);
awaiterThread2.join();
assertTrue(awaiterThread1.isAlive());
assertTrue(awaiterThread3.isAlive());
future5.setResult(null);
awaiterThread1.join();
awaiterThread3.join();
assertNull(awaiterTask3.getInterruptedException());
}
public void testCast() throws Exception {
// future with result set
final TestIoFuture<Object> future1 = new TestIoFuture<Object>();
final IoFuture<? extends String> castFuture1 = IoUtils.<Object, String>cast(future1, String.class);
assertSame(Status.WAITING, castFuture1.getStatus());
assertSame(Status.WAITING, castFuture1.await(10, TimeUnit.MICROSECONDS));
assertSame(Status.WAITING, castFuture1.awaitInterruptibly(1, TimeUnit.MICROSECONDS));
future1.setResult("test");
assertSame(Status.DONE, castFuture1.getStatus());
assertSame(Status.DONE, castFuture1.await(10, TimeUnit.MICROSECONDS));
assertSame(Status.DONE, castFuture1.awaitInterruptibly(1, TimeUnit.MICROSECONDS));
assertSame(Status.DONE, castFuture1.await());
assertSame(Status.DONE, castFuture1.awaitInterruptibly());
assertSame("test", castFuture1.get());
assertSame("test", castFuture1.getInterruptibly());
final TestCloseable closeable1 = new TestCloseable();
castFuture1.addNotifier(IoUtils.attachmentClosingNotifier(), closeable1);
assertFalse(closeable1.isOpen());
// cancelled future
final TestIoFuture<Object> future2 = new TestIoFuture<Object>();
final IoFuture<? extends String> castFuture2 = IoUtils.<Object, String>cast(future2, String.class);
final TestCloseable closeable2 = new TestCloseable();
castFuture2.addNotifier(IoUtils.attachmentClosingNotifier(), closeable2);
assertTrue(closeable2.isOpen());
assertSame(Status.WAITING, castFuture2.getStatus());
assertSame(Status.WAITING, castFuture2.await(10, TimeUnit.MICROSECONDS));
assertSame(Status.WAITING, castFuture2.awaitInterruptibly(1, TimeUnit.MICROSECONDS));
castFuture2.cancel();
assertSame(Status.CANCELLED, castFuture2.getStatus());
assertSame(Status.CANCELLED, castFuture2.await(10, TimeUnit.MICROSECONDS));
assertSame(Status.CANCELLED, castFuture2.awaitInterruptibly(1, TimeUnit.MICROSECONDS));
assertSame(Status.CANCELLED, castFuture2.await());
assertSame(Status.CANCELLED, castFuture2.awaitInterruptibly());
assertFalse(closeable2.isOpen());
CancellationException expected = null;
try {
castFuture2.get();
} catch (CancellationException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
castFuture2.getInterruptibly();
} catch (CancellationException e) {
expected = e;
}
assertNotNull(expected);
// failed future
final TestIoFuture<Object> future3 = new TestIoFuture<Object>();
final IoFuture<? extends String> castFuture3 = IoUtils.<Object, String>cast(future3, String.class);
assertSame(Status.WAITING, castFuture3.getStatus());
assertSame(Status.WAITING, castFuture3.await(10, TimeUnit.MICROSECONDS));
assertSame(Status.WAITING, castFuture3.awaitInterruptibly(1, TimeUnit.MICROSECONDS));
final IOException failure = new IOException("Test exception");
future3.setException(failure);
assertSame(Status.FAILED, castFuture3.getStatus());
assertSame(Status.FAILED, castFuture3.await(10, TimeUnit.MICROSECONDS));
assertSame(Status.FAILED, castFuture3.awaitInterruptibly(1, TimeUnit.MICROSECONDS));
assertSame(Status.FAILED, castFuture3.await());
assertSame(Status.FAILED, castFuture3.awaitInterruptibly());
assertSame(failure, castFuture3.getException());
}
public void testSafeShutdownReads() {
final ChannelMock channel1 = new ChannelMock();
final ChannelMock channel2 = new ChannelMock();
IoUtils.safeShutdownReads(channel1);
assertTrue(channel1.isShutdownReads());
channel2.throwExceptionOnShutdownReads();
IoUtils.safeShutdownReads(channel2);
assertFalse(channel2.isShutdownReads());
IoUtils.safeShutdownReads(null); // should just ignore
}
public void testTransfer() throws IOException {
final ConnectedStreamChannelMock sourceChannel = new ConnectedStreamChannelMock();
final ConnectedStreamChannelMock sinkChannel = new ConnectedStreamChannelMock();
sinkChannel.enableWrite(false);
sourceChannel.setReadData("a kinda big text to transfer from source to sink");
sourceChannel.enableRead(true);
final ByteBuffer throughBuffer = ByteBuffer.allocate(10);
assertEquals(0, IoUtils.transfer(sourceChannel, 50, throughBuffer, sinkChannel));
assertWrittenMessage(sinkChannel);
sinkChannel.enableWrite(true);
sinkChannel.write(throughBuffer);
assertEquals(38, IoUtils.transfer(sourceChannel, 60, throughBuffer, sinkChannel));
assertWrittenMessage(sinkChannel, "a kinda big text to transfer from source to sink");
}
public void testManagerNotifier() throws Exception {
// add manager notifier to a future that will be cancelled
final TestIoFuture<Channel> future1 = new TestIoFuture<Channel>();
final FutureResult<Channel> manager1 = new FutureResult<Channel>();
final FutureResult<Channel> manager2 = new FutureResult<Channel>();
final IoFuture.Notifier<Channel, FutureResult<Channel>> notifier1 = IoUtils.<Channel>getManagerNotifier();
final IoFuture.Notifier<Channel, FutureResult<Channel>> notifier2 = IoUtils.<Channel>getManagerNotifier();
future1.addNotifier(notifier1, manager1);
future1.cancel();
future1.addNotifier(notifier2, manager2);
assertSame(Status.CANCELLED, manager1.getIoFuture().getStatus());
assertSame(Status.CANCELLED, manager2.getIoFuture().getStatus());
// add manager notifier to a future that will fail
final IOException exception = new IOException("Test exception");
final TestIoFuture<Channel> future2 = new TestIoFuture<Channel>();
final FutureResult<Channel> manager3 = new FutureResult<Channel>();
final FutureResult<Channel> manager4 = new FutureResult<Channel>();
final IoFuture.Notifier<Channel, FutureResult<Channel>> notifier3 = IoUtils.<Channel>getManagerNotifier();
final IoFuture.Notifier<Channel, FutureResult<Channel>> notifier4 = IoUtils.<Channel>getManagerNotifier();
future2.addNotifier(notifier3, manager3);
future2.setException(exception);
future2.addNotifier(notifier4, manager4);
assertSame(Status.FAILED, manager3.getIoFuture().getStatus());
assertSame(exception, manager3.getIoFuture().getException());
assertSame(Status.FAILED, manager4.getIoFuture().getStatus());
assertSame(exception, manager4.getIoFuture().getException());
// add manager notifier to a future that will have its value set
final Channel result = new ConnectedStreamChannelMock();
final TestIoFuture<Channel> future3 = new TestIoFuture<Channel>();
final FutureResult<Channel> manager5 = new FutureResult<Channel>();
final FutureResult<Channel> manager6 = new FutureResult<Channel>();
final IoFuture.Notifier<Channel, FutureResult<Channel>> notifier5 = IoUtils.<Channel>getManagerNotifier();
final IoFuture.Notifier<Channel, FutureResult<Channel>> notifier6 = IoUtils.<Channel>getManagerNotifier();
future3.addNotifier(notifier5, manager5);
future3.setResult(result);
future3.addNotifier(notifier6, manager6);
assertSame(Status.DONE, manager5.getIoFuture().getStatus());
assertSame(result, manager5.getIoFuture().get());
assertSame(Status.DONE, manager6.getIoFuture().getStatus());
assertSame(result, manager6.getIoFuture().get());
}
public void testRetryingChannelSource() throws Exception {
final TestChannelSource testChannelSource1 = new TestChannelSource(0);
final TestChannelSource testChannelSource2 = new TestChannelSource(3);
final TestChannelSource testChannelSource3 = new TestChannelSource(8);
final ChannelSource<ConnectedStreamChannelMock> testChannelSource4 = new ChannelSource<ConnectedStreamChannelMock>() {
@Override
public IoFuture<ConnectedStreamChannelMock> open(ChannelListener<? super ConnectedStreamChannelMock> openListener) {
return new TestIoFuture<ConnectedStreamChannelMock>().cancel();
}
};
final ChannelSource<ConnectedStreamChannelMock> retryingChannelSource1 = IoUtils.getRetryingChannelSource(testChannelSource1, 5);
final ChannelSource<ConnectedStreamChannelMock> retryingChannelSource2 = IoUtils.getRetryingChannelSource(testChannelSource2, 5);
final ChannelSource<ConnectedStreamChannelMock> retryingChannelSource3 = IoUtils.getRetryingChannelSource(testChannelSource3, 5);
final ChannelSource<ConnectedStreamChannelMock> retryingChannelSource4 = IoUtils.getRetryingChannelSource(testChannelSource4, 5);
final TestOpenListener openListener1 = new TestOpenListener();
final TestOpenListener openListener2 = new TestOpenListener();
final TestOpenListener openListener3 = new TestOpenListener();
final TestOpenListener openListener4 = new TestOpenListener();
final IoFuture<ConnectedStreamChannelMock> future1 = retryingChannelSource1.open(openListener1);
final IoFuture<ConnectedStreamChannelMock> future2 = retryingChannelSource2.open(openListener2);
final IoFuture<ConnectedStreamChannelMock> future3 = retryingChannelSource3.open(openListener3);
final IoFuture<ConnectedStreamChannelMock> future4 = retryingChannelSource4.open(openListener4);
assertSame(Status.DONE, future1.getStatus());
assertNotNull(future1.get());
assertTrue(openListener1.isInvoked());
assertSame(future1.get(), openListener1.getChannel());
assertSame(Status.DONE, future2.getStatus());
assertNotNull(future2.get());
assertTrue(openListener2.isInvoked());
assertSame(future2.get(), openListener2.getChannel());
assertSame(Status.FAILED, future3.getStatus());
assertNotNull(future3.getException());
assertFalse(openListener3.isInvoked());
assertSame(Status.CANCELLED, future4.getStatus());
assertFalse(openListener3.isInvoked());
IllegalArgumentException expected = null;
try {
IoUtils.getRetryingChannelSource(testChannelSource1, 0);
} catch (IllegalArgumentException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
IoUtils.getRetryingChannelSource(testChannelSource1, -1);
} catch (IllegalArgumentException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
IoUtils.getRetryingChannelSource(testChannelSource1, -9);
} catch (IllegalArgumentException e) {
expected = e;
}
assertNotNull(expected);
}
public void testClosingCancellable() {
final TestCloseable closeable = new TestCloseable();
final Cancellable closingCancellable = IoUtils.closingCancellable(closeable);
assertNotNull(closingCancellable);
assertTrue(closeable.isOpen());
assertSame(closingCancellable, closingCancellable.cancel());
assertFalse(closeable.isOpen());
}
public void testThreadLocalRandom() throws Exception {
final Random random = IoUtils.getThreadLocalRandom();
random.nextFloat();
random.nextInt();
final ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
final ObjectOutput objectOutput = new ObjectOutputStream(new BufferedOutputStream(byteOutput));
objectOutput.writeObject(random);
objectOutput.close();
final ObjectInput objectInput = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(byteOutput.toByteArray())));
final Random deserializedRandom = (Random) objectInput.readObject();
assertNotNull(deserializedRandom);
}
public void testTransferThroughBuffer() throws IOException{
byte[] bytes = "This bytes".getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteBuffer buffer = ByteBuffer.allocate(16);
assertEquals(bytes.length, IoUtils.transfer(Channels.newChannel(in), bytes.length, buffer, Channels.newChannel(out)));
assertFalse(buffer.hasRemaining());
}
private static abstract class TestSelector extends Selector {
@Override
public boolean isOpen() {
return false;
}
@Override
public SelectorProvider provider() {
return null;
}
@Override
public Set<SelectionKey> keys() {
return null;
}
@Override
public Set<SelectionKey> selectedKeys() {
return null;
}
@Override
public int selectNow() throws IOException {
return 0;
}
@Override
public int select(long timeout) throws IOException {
return 0;
}
@Override
public int select() throws IOException {
return 0;
}
@Override
public Selector wakeup() {
return null;
}
}
private static class TestIoFuture<T> extends AbstractIoFuture<T> {
@Override
public boolean setResult(T result) {
return super.setResult(result);
}
@Override
public boolean setException(IOException exception) {
return super.setException(exception);
}
@Override
public TestIoFuture<T> cancel() {
super.cancel();
setCancelled();
return this;
}
}
private static class TestCloseable implements Closeable {
private boolean open = true;
public void close() {
open = false;
}
public boolean isOpen() {
return open;
}
}
private static class TestRunnable implements Runnable {
private boolean invoked = false;
public void run() {
invoked = true;
}
public boolean isInvoked() {
return invoked;
}
}
private static class TestChannelListener implements ChannelListener<ConnectedStreamChannel> {
private boolean invoked = false;
private ConnectedStreamChannel channel;
@Override
public void handleEvent(ConnectedStreamChannel c) {
invoked = true;
channel = c;
}
public boolean isInvoked() {
return invoked;
}
public ConnectedStreamChannel getChannel() {
return channel;
}
}
private static class FutureValueRetriever<T> implements Runnable {
private final Future<T> future;
private final long timeout;
private final TimeUnit timeoutUnit;
private volatile T result;
private TimeoutException timeoutException = null;
private CancellationException cancellationException = null;
private IOException failure;
public FutureValueRetriever(Future<T> f) {
this(f, -1, null);
}
public FutureValueRetriever(Future<T> f, long t, TimeUnit tu) {
future = f;
timeout = t;
timeoutUnit = tu;
}
public void run() {
try {
if (timeoutUnit == null) {
result = future.get();
} else {
result = future.get(timeout, timeoutUnit);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
if (e.getCause() instanceof IOException) {
failure = (IOException) e.getCause();
} else {
throw new RuntimeException(e);
}
} catch (TimeoutException e) {
timeoutException = e;
} catch (CancellationException e) {
cancellationException = e;
}
}
public T getFutureValue() {
return result;
}
public TimeoutException getTimeoutException() {
return timeoutException;
}
public CancellationException getCancellationException() {
return cancellationException;
}
public IOException getFailure() {
return failure;
}
}
private class Awaiter implements Runnable {
private boolean interruptibly;
private IoFuture<?>[] futures;
private InterruptedException exception;
public Awaiter(IoFuture<?>... f) {
this(false, f);
}
public Awaiter(boolean i, IoFuture<?>... f) {
futures = f;
interruptibly = i;
}
public void run() {
try {
if (interruptibly) {
IoUtils.awaitAllInterruptibly(futures);
} else {
IoUtils.awaitAll(futures);
}
} catch (InterruptedException e) {
e.printStackTrace();
exception = e;
} finally {}
}
public InterruptedException getInterruptedException() {
return exception;
}
}
private static class ChannelMock extends ConnectedStreamChannelMock {
private boolean throwExceptionOnShutdownReads = false;
public void throwExceptionOnShutdownReads() {
throwExceptionOnShutdownReads = true;
}
@Override
public void shutdownReads() throws IOException {
if (throwExceptionOnShutdownReads) {
throw new IOException("Test exception");
}
super.shutdownReads();
}
}
private static class TestChannelSource implements ChannelSource<ConnectedStreamChannelMock> {
public int count;
public TestChannelSource(int numberOfFailures) {
count = numberOfFailures;
}
@Override
public IoFuture<ConnectedStreamChannelMock> open(ChannelListener<? super ConnectedStreamChannelMock> openListener) {
if (count == 0) {
IoFuture<ConnectedStreamChannelMock> future = new FinishedIoFuture<ConnectedStreamChannelMock>(new ConnectedStreamChannelMock());
try {
openListener.handleEvent(future.get());
} catch (CancellationException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
return future;
}
count --;
return new FailedIoFuture<ConnectedStreamChannelMock>(new IOException("Test exception"));
}
}
private static class TestOpenListener implements ChannelListener<ConnectedStreamChannelMock> {
private boolean invoked;
private ConnectedStreamChannelMock channel;
@Override
public void handleEvent(ConnectedStreamChannelMock c) {
invoked = true;
channel = c;
}
public boolean isInvoked() {
return invoked;
}
public ConnectedStreamChannelMock getChannel() {
return channel;
}
}
}