/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xnio.nio.test;
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.assertTrue;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.xnio.Buffers;
import org.xnio.ChannelPipe;
import org.xnio.Option;
import org.xnio.OptionMap;
import org.xnio.Options;
import org.xnio.XnioWorker;
import org.xnio.channels.StreamChannel;
/**
* Tests a full duplex channel pipe.
*
* @author <a href="mailto:flavia.rainone@jboss.com">Flavia Rainone</a>
*
*/
public class FullDuplexChannelPipeTestCase extends AbstractNioStreamChannelTest {
// for a matter of code readability, it is better to name those channels channel1/channel2 in the super class
// and create new fields in this subclass so we can use a more appropriate name here
private StreamChannel leftChannel;
private StreamChannel rightChannel;
@Before
public void initChannels() throws IOException {
super.initChannels();
}
@Override
protected void initChannels(XnioWorker xnioWorker, OptionMap optionMap, TestChannelListener<StreamChannel> leftChannelListener,
TestChannelListener<StreamChannel> rightChannelListener) throws IOException {
if (leftChannel != null) {
closeChannels();
}
final ChannelPipe<StreamChannel, StreamChannel> pipeChannel = xnioWorker.createFullDuplexPipe();
assertNotNull(pipeChannel);
assertNotNull(pipeChannel.toString());
leftChannel = pipeChannel.getLeftSide();
rightChannel = pipeChannel.getRightSide();
assertNotNull(leftChannel);
assertNotNull(leftChannel.toString());
assertNotNull(rightChannel);
assertNotNull(rightChannel.toString());
leftChannelListener.handleEvent(leftChannel);
rightChannelListener.handleEvent(rightChannel);
}
@Test
public void writeAndReadBufferAndClose() throws IOException {
final TestChannelListener<StreamChannel> leftWriteListener = new TestChannelListener<StreamChannel>();
final TestChannelListener<StreamChannel> rightWriteListener = new TestChannelListener<StreamChannel>();
leftChannel.getWriteSetter().set(leftWriteListener);
rightChannel.getWriteSetter().set(rightWriteListener);
// Step 1: communicate several times using a ByteBuffer
final ByteBuffer buffer = ByteBuffer.allocate(5);
buffer.put("12345".getBytes()).flip();
assertEquals(5, leftChannel.write(buffer));
buffer.clear();
assertEquals(5, rightChannel.read(buffer));
buffer.flip();
assertEquals("12345", Buffers.getModifiedUtf8(buffer));
buffer.clear();
buffer.put("67890".getBytes()).flip();
assertEquals(5, rightChannel.write(buffer));
buffer.clear();
assertEquals(5, leftChannel.read(buffer));
buffer.flip();
assertEquals("67890", Buffers.getModifiedUtf8(buffer));
assertFalse(leftChannel.isWriteResumed());
leftChannel.resumeWrites();
assertFalse(rightChannel.isWriteResumed());
rightChannel.resumeWrites();
assertFalse(leftChannel.isReadResumed());
leftChannel.resumeReads();
assertFalse(rightChannel.isReadResumed());
rightChannel.resumeReads();
assertTrue(leftWriteListener.isInvoked());
assertTrue(rightWriteListener.isInvoked());
// Step 2: close leftChannel
leftChannel.close();
assertFalse(leftChannel.isWriteResumed());
assertFalse(leftChannel.isReadResumed());
Exception expected = null;
try {
leftChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
assertEquals(0, rightChannel.write(buffer));
buffer.flip();
expected = null;
try {
rightChannel.write(buffer);
} catch(IOException e) { // broken pipe
expected = e;
}
assertNotNull(expected);
buffer.clear();
assertEquals(-1, leftChannel.read(buffer));
assertEquals(-1, rightChannel.read(buffer));
// Step 3: close rightChannel
assertFalse(leftChannel.isOpen());
assertTrue(rightChannel.isOpen());
rightChannel.close();
assertFalse(leftChannel.isOpen());
assertFalse(rightChannel.isOpen());
assertFalse(rightChannel.isWriteResumed());
assertFalse(rightChannel.isReadResumed());
expected = null;
try {
leftChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
rightChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
buffer.clear();
assertEquals(-1, leftChannel.read(buffer));
assertEquals(-1, rightChannel.read(buffer));
// Step 4: idempotent close
assertFalse(leftChannel.isOpen());
assertFalse(rightChannel.isOpen());
rightChannel.close();
leftChannel.close();
assertFalse(leftChannel.isOpen());
assertFalse(rightChannel.isOpen());
expected = null;
try {
leftChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
rightChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
buffer.clear();
assertEquals(-1, leftChannel.read(buffer));
assertEquals(-1, rightChannel.read(buffer));
// Step 5: shutdown read and write, should be idempotent
leftChannel.shutdownReads();
leftChannel.shutdownWrites();
rightChannel.shutdownReads();
rightChannel.shutdownWrites();
assertFalse(leftChannel.isOpen());
assertFalse(rightChannel.isOpen());
expected = null;
try {
leftChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
rightChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
buffer.clear();
assertEquals(-1, leftChannel.read(buffer));
assertEquals(-1, rightChannel.read(buffer));
}
@Test
public void writeAndReadMultipleBuffersAndShutdown() throws IOException {
final TestChannelListener<StreamChannel> leftWriteListener = new TestChannelListener<StreamChannel>();
final TestChannelListener<StreamChannel> rightWriteListener = new TestChannelListener<StreamChannel>();
final TestChannelListener<StreamChannel> leftReadListener = new TestChannelListener<StreamChannel>();
final TestChannelListener<StreamChannel> rightReadListener = new TestChannelListener<StreamChannel>();
leftChannel.getWriteSetter().set(leftWriteListener);
rightChannel.getWriteSetter().set(rightWriteListener);
leftChannel.getReadSetter().set(leftReadListener);
rightChannel.getReadSetter().set(rightReadListener);
// Step 1: communicate several times using a ByteBuffer[]
final ByteBuffer[] buffers = new ByteBuffer[] {ByteBuffer.allocate(5), ByteBuffer.allocate(1), ByteBuffer.allocate(2)};
buffers[0].put("12345".getBytes()).flip();
buffers[1].put("6".getBytes()).flip();
buffers[2].put("78".getBytes()).flip();
assertEquals(8, leftChannel.write(buffers));
buffers[0].clear();
buffers[1].clear();
buffers[2].clear();
assertEquals(8, rightChannel.read(buffers));
buffers[0].flip();
assertEquals("12345", Buffers.getModifiedUtf8(buffers[0]));
buffers[1].flip();
assertEquals('6', buffers[1].get(0));
buffers[2].flip();
assertEquals("78", Buffers.getModifiedUtf8(buffers[2]));
buffers[0].clear();
buffers[0].put("09876".getBytes()).flip();
buffers[1].clear();
buffers[1].put("5".getBytes()).flip();
buffers[2].clear();
buffers[2].put("43".getBytes()).flip();
assertEquals(8, rightChannel.write(buffers));
buffers[0].clear();
buffers[1].clear();
buffers[2].clear();
assertEquals(8, leftChannel.read(buffers));
buffers[0].flip();
assertEquals("09876", Buffers.getModifiedUtf8(buffers[0]));
buffers[1].flip();
assertEquals('5', buffers[1].get(0));
buffers[2].flip();
assertEquals("43", Buffers.getModifiedUtf8(buffers[2]));
assertFalse(leftChannel.isWriteResumed());
leftChannel.resumeWrites();
assertFalse(rightChannel.isWriteResumed());
rightChannel.resumeWrites();
assertFalse(leftChannel.isReadResumed());
leftChannel.resumeReads();
assertFalse(rightChannel.isReadResumed());
rightChannel.resumeReads();
assertTrue(leftWriteListener.isInvoked());
assertTrue(rightWriteListener.isInvoked());
// Step 2: shutdownReads on rightChannel
rightChannel.shutdownReads();
assertFalse(rightChannel.isReadResumed());
assertTrue(rightChannel.isWriteResumed());
assertTrue(leftChannel.isOpen());
assertTrue(rightChannel.isOpen());
rightChannel.awaitReadable();
rightChannel.awaitReadable(30, TimeUnit.SECONDS);
assertEquals(1, rightChannel.write(buffers));
assertEquals(-1, rightChannel.read(buffers));
buffers[0].flip();
buffers[1].flip();
buffers[2].flip();
Exception expected = null;
try {
assertEquals(1, leftChannel.write(buffers));
} catch (IOException e) { // broken pipe
expected = e;
}
assertNotNull(expected);
buffers[0].clear();
buffers[1].clear();
buffers[2].clear();
assertEquals(1, leftChannel.read(buffers));
buffers[0].flip();
assertEquals('5', buffers[0].get(0));
// Step 3: shutdownWrites on leftChannel
leftChannel.shutdownWrites();
assertTrue(leftChannel.isReadResumed());
assertFalse(leftChannel.isWriteResumed());
assertTrue(leftChannel.isOpen());
leftChannel.awaitWritable();
leftChannel.awaitWritable(1, TimeUnit.DAYS);
expected = null;
try {
leftChannel.write(buffers);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
assertEquals(4, rightChannel.write(buffers));
buffers[0].clear();
buffers[1].clear();
buffers[2].clear();
assertEquals(4, leftChannel.read(buffers));
buffers[0].flip();
assertEquals("5543", Buffers.getModifiedUtf8(buffers[0]));
buffers[1].flip();
assertFalse(buffers[1].hasRemaining());
buffers[2].flip();
assertFalse(buffers[2].hasRemaining());
assertEquals(-1, rightChannel.read(ByteBuffer.allocate(5)));
// Step 4: shutdownWrites on rightChannel
rightChannel.shutdownWrites();
assertFalse(rightChannel.isOpen());
assertFalse(rightChannel.isReadResumed());
assertFalse(rightChannel.isWriteResumed());
rightChannel.awaitWritable();
rightChannel.awaitWritable(1, TimeUnit.DAYS);
buffers[0].flip();
expected = null;
try {
rightChannel.write(buffers);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
leftChannel.write(buffers);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
buffers[0].clear();
assertEquals(-1, rightChannel.read(buffers));
assertEquals(-1, leftChannel.read(buffers));
// Step 5: shutdownReads on leftChannel
assertTrue(leftChannel.isOpen());
leftChannel.shutdownReads();
assertFalse(leftChannel.isOpen());
assertFalse(leftChannel.isReadResumed());
assertFalse(leftChannel.isWriteResumed());
leftChannel.awaitReadable();
leftChannel.awaitReadable(30, TimeUnit.SECONDS);
expected = null;
try {
leftChannel.write(buffers);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
rightChannel.write(buffers);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
assertEquals(-1, leftChannel.read(buffers));
assertEquals(-1, rightChannel.read(buffers));
// Step 6: idempotent shutdown operations
leftChannel.shutdownReads();
leftChannel.shutdownWrites();
rightChannel.shutdownReads();
rightChannel.shutdownWrites();
assertFalse(leftChannel.isOpen());
assertFalse(rightChannel.isOpen());
expected = null;
try {
rightChannel.write(buffers);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
leftChannel.write(buffers);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
assertEquals(-1, rightChannel.read(buffers));
assertEquals(-1, leftChannel.read(buffers));
// Step 7: close channels, should be idempotent
leftChannel.close();
rightChannel.close();
assertFalse(leftChannel.isOpen());
assertFalse(rightChannel.isOpen());
expected = null;
try {
rightChannel.write(buffers);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
leftChannel.write(buffers);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
assertEquals(-1, rightChannel.read(buffers));
assertEquals(-1, leftChannel.read(buffers));
}
@Test
public void closePartiallyShutdownChannel() throws IOException {
final ByteBuffer buffer = ByteBuffer.allocate(3);
final TestChannelListener<StreamChannel> leftWriteListener = new TestChannelListener<StreamChannel>();
final TestChannelListener<StreamChannel> rightWriteListener = new TestChannelListener<StreamChannel>();
final TestChannelListener<StreamChannel> leftReadListener = new TestChannelListener<StreamChannel>();
final TestChannelListener<StreamChannel> rightReadListener = new TestChannelListener<StreamChannel>();
leftChannel.getWriteSetter().set(leftWriteListener);
rightChannel.getWriteSetter().set(rightWriteListener);
leftChannel.getReadSetter().set(leftReadListener);
rightChannel.getReadSetter().set(rightReadListener);
assertFalse(leftChannel.isWriteResumed());
leftChannel.resumeWrites();
assertFalse(rightChannel.isWriteResumed());
rightChannel.resumeWrites();
assertFalse(leftChannel.isReadResumed());
leftChannel.resumeReads();
assertFalse(rightChannel.isReadResumed());
rightChannel.resumeReads();
assertTrue(leftWriteListener.isInvoked());
assertTrue(rightWriteListener.isInvoked());
// Step 1: shutdownReads on leftChannel
leftChannel.shutdownReads();
assertFalse(leftChannel.isReadResumed());
leftChannel.awaitReadable();
leftChannel.awaitReadable(10, TimeUnit.SECONDS);
assertTrue(leftChannel.isWriteResumed());
assertTrue(leftChannel.isOpen());
assertTrue(rightChannel.isOpen());
assertEquals(3, leftChannel.write(buffer));
buffer.flip();
assertEquals(3, rightChannel.read(buffer));
buffer.flip();
Exception expected = null;
try {
rightChannel.write(buffer);
} catch (IOException e) { // broken pipe
expected = e;
}
assertNotNull(expected);
buffer.clear();
assertEquals(-1, leftChannel.read(buffer));
// Step 2: shutdownWrites on rightChannel
rightChannel.shutdownWrites();
assertTrue(rightChannel.isReadResumed());
assertFalse(rightChannel.isWriteResumed());
assertTrue(rightChannel.isOpen());
rightChannel.awaitWritable();
rightChannel.awaitWritable(1, TimeUnit.MINUTES);
expected = null;
try {
rightChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
assertEquals(3, leftChannel.write(buffer));
buffer.clear();
assertEquals(3, rightChannel.read(buffer));
assertEquals(-1, leftChannel.read(ByteBuffer.allocate(5)));
// Step 3: close leftChannel
leftChannel.close();
assertFalse(leftChannel.isOpen());
assertFalse(leftChannel.isReadResumed());
assertFalse(leftChannel.isWriteResumed());
leftChannel.awaitReadable();
leftChannel.awaitReadable(10, TimeUnit.SECONDS);
leftChannel.awaitWritable();
leftChannel.awaitWritable(1, TimeUnit.MINUTES);
buffer.flip();
expected = null;
try {
leftChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
rightChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
assertEquals(-1, rightChannel.read(buffer));
assertEquals(-1, leftChannel.read(buffer));
// Step 4: close rightChannel
assertTrue(rightChannel.isOpen());
rightChannel.close();
assertFalse(rightChannel.isOpen());
assertFalse(rightChannel.isReadResumed());
assertFalse(rightChannel.isWriteResumed());
rightChannel.awaitReadable();
rightChannel.awaitReadable(10, TimeUnit.SECONDS);
rightChannel.awaitWritable();
rightChannel.awaitWritable(1, TimeUnit.MINUTES);
expected = null;
try {
leftChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
rightChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
assertEquals(-1, leftChannel.read(buffer));
assertEquals(-1, rightChannel.read(buffer));
// Step 5: idempotent shutdown and close operations
leftChannel.shutdownReads();
leftChannel.shutdownWrites();
rightChannel.shutdownReads();
rightChannel.shutdownWrites();
leftChannel.close();
rightChannel.close();
assertFalse(leftChannel.isOpen());
assertFalse(rightChannel.isOpen());
expected = null;
try {
rightChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
expected = null;
try {
leftChannel.write(buffer);
} catch (ClosedChannelException e) {
expected = e;
}
assertNotNull(expected);
assertEquals(-1, rightChannel.read(buffer));
assertEquals(-1, leftChannel.read(buffer));
}
@Test
public void optionSetup() throws IOException {
initChannels();
final Option<?>[] unsupportedOptions = OptionHelper.getNotSupportedOptions(Options.READ_TIMEOUT,
Options.WRITE_TIMEOUT);
for (Option<?> option: unsupportedOptions) {
assertFalse("Channel supports " + option, leftChannel.supportsOption(option));
assertNull("Expected null value for option " + option + " but got " + leftChannel.getOption(option) +
" instead", leftChannel.getOption(option));
}
assertTrue(leftChannel.supportsOption(Options.READ_TIMEOUT));
assertTrue(leftChannel.supportsOption(Options.WRITE_TIMEOUT));
leftChannel.setOption(Options.READ_TIMEOUT, 39710);
leftChannel.setOption(Options.WRITE_TIMEOUT, 1301093);
assertNull("Unexpected option value: " + leftChannel.getOption(Options.MAX_INBOUND_MESSAGE_SIZE),
leftChannel.setOption(Options.MAX_INBOUND_MESSAGE_SIZE, 50000));// unsupported
assertEquals(39710, (int) leftChannel.getOption(Options.READ_TIMEOUT));
assertEquals(1301093, (int) leftChannel.getOption(Options.WRITE_TIMEOUT));
assertNull(leftChannel.getOption(Options.MAX_INBOUND_MESSAGE_SIZE));// unsupported
assertEquals(39710, (int) leftChannel.setOption(Options.READ_TIMEOUT, 70977010));
assertEquals(1301093, (int) leftChannel.setOption(Options.WRITE_TIMEOUT, 293265));
assertEquals(293265, (int) leftChannel.getOption(Options.WRITE_TIMEOUT));
assertEquals(70977010, (int) leftChannel.getOption(Options.READ_TIMEOUT));
// XNIO-171 test setOption(*, null)?
}
}