/* * 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.StreamSinkChannel; import org.xnio.channels.StreamSourceChannel; /** * Tests a half duplex channel pipe. * * @author <a href="mailto:flavia.rainone@jboss.com">Flavia Rainone</a> * */ public class HalfDuplexChannelPipeTestCase extends AbstractStreamSinkSourceChannelTest<StreamSinkChannel, StreamSourceChannel> { @Override protected void initChannels(XnioWorker xnioWorker, OptionMap optionMap, TestChannelListener<StreamSinkChannel> sinkChannelListener, TestChannelListener<StreamSourceChannel> sourceChannelListener) throws IOException { final ChannelPipe<StreamSourceChannel, StreamSinkChannel> pipeChannel = xnioWorker.createHalfDuplexPipe(); sinkChannelListener.handleEvent(pipeChannel.getRightSide()); sourceChannelListener.handleEvent(pipeChannel.getLeftSide()); } @Before public void initChannels() throws IOException { super.initChannels(); } @Test public void writeAndReadBufferAndClose() throws IOException { final TestChannelListener<StreamSinkChannel> writeListener = new TestChannelListener<StreamSinkChannel>(); sinkChannel.getWriteSetter().set(writeListener); // Step 1: communicate several times using a ByteBuffer final ByteBuffer buffer = ByteBuffer.allocate(5); buffer.put("12345".getBytes()).flip(); assertEquals(5, sinkChannel.write(buffer)); buffer.clear(); assertEquals(5, sourceChannel.read(buffer)); buffer.flip(); assertEquals("12345", Buffers.getModifiedUtf8(buffer)); buffer.clear(); buffer.put("67890".getBytes()).flip(); assertEquals(5, sinkChannel.write(buffer)); buffer.clear(); assertEquals(5, sourceChannel.read(buffer)); buffer.flip(); assertEquals("67890", Buffers.getModifiedUtf8(buffer)); assertFalse(sinkChannel.isWriteResumed()); sinkChannel.resumeWrites(); assertTrue(writeListener.isInvoked()); // Step 2: close sinkChannel sinkChannel.close(); assertFalse(sinkChannel.isWriteResumed()); Exception expected = null; try { sinkChannel.write(buffer); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); buffer.clear(); assertEquals(-1, sourceChannel.read(buffer)); // Step 3: close sourceChannel assertFalse(sinkChannel.isOpen()); assertTrue(sourceChannel.isOpen()); sourceChannel.close(); assertFalse(sinkChannel.isOpen()); assertFalse(sourceChannel.isOpen()); assertFalse(sourceChannel.isReadResumed()); expected = null; try { sinkChannel.write(buffer); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); buffer.clear(); assertEquals(-1, sourceChannel.read(buffer)); // Step 4: idempotent close assertFalse(sinkChannel.isOpen()); assertFalse(sourceChannel.isOpen()); sourceChannel.close(); sinkChannel.close(); assertFalse(sinkChannel.isOpen()); assertFalse(sourceChannel.isOpen()); expected = null; try { sinkChannel.write(buffer); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); buffer.clear(); assertEquals(-1, sourceChannel.read(buffer)); // Step 5: shutdown read and write, should be idempotent sourceChannel.shutdownReads(); sinkChannel.shutdownWrites(); assertFalse(sinkChannel.isOpen()); assertFalse(sourceChannel.isOpen()); expected = null; try { sinkChannel.write(buffer); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); buffer.clear(); assertEquals(-1, sourceChannel.read(buffer)); } @Test public void writeAndReadMultipleBuffersAndShutdown() throws IOException { final TestChannelListener<StreamSinkChannel> writeListener = new TestChannelListener<StreamSinkChannel>(); final TestChannelListener<StreamSourceChannel> readListener = new TestChannelListener<StreamSourceChannel>(); sinkChannel.getWriteSetter().set(writeListener); sourceChannel.getReadSetter().set(readListener); // 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, sinkChannel.write(buffers)); buffers[0].clear(); buffers[1].clear(); buffers[2].clear(); assertEquals(8, sourceChannel.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, sinkChannel.write(buffers)); buffers[0].clear(); buffers[1].clear(); buffers[2].clear(); assertEquals(8, sourceChannel.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])); assertTrue(sinkChannel.flush()); assertFalse(sinkChannel.isWriteResumed()); sinkChannel.resumeWrites(); assertFalse(sourceChannel.isReadResumed()); sourceChannel.resumeReads(); assertTrue(writeListener.isInvoked()); // Step 2: shutdownReads on sourceChannel sourceChannel.shutdownReads(); assertFalse(sourceChannel.isReadResumed()); assertFalse(sourceChannel.isOpen()); sourceChannel.awaitReadable(); sourceChannel.awaitReadable(30, TimeUnit.SECONDS); IOException expected = null; try { sinkChannel.write(buffers); } catch (IOException e) { // broken pipe expected = e; } assertNotNull(expected); assertEquals(-1, sourceChannel.read(buffers)); // Step 3: shutdownWrites on sinkChannel sinkChannel.shutdownWrites(); assertFalse(sinkChannel.isWriteResumed()); assertFalse(sinkChannel.isOpen()); sinkChannel.awaitWritable(); sinkChannel.awaitWritable(1, TimeUnit.DAYS); expected = null; try { sinkChannel.write(buffers); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); assertEquals(-1, sourceChannel.read(ByteBuffer.allocate(5))); // Step 4: idempotent shutdown operations sourceChannel.shutdownReads(); sinkChannel.shutdownWrites(); assertFalse(sinkChannel.isOpen()); assertFalse(sourceChannel.isOpen()); expected = null; try { sinkChannel.write(buffers); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); assertEquals(-1, sourceChannel.read(buffers)); // Step 5: close channels, should be idempotent sinkChannel.close(); sinkChannel.close(); assertFalse(sinkChannel.isOpen()); assertFalse(sourceChannel.isOpen()); expected = null; try { sinkChannel.write(buffers); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); assertEquals(-1, sourceChannel.read(buffers)); } @Test public void closeShutdownChannel() throws IOException { final ByteBuffer buffer = ByteBuffer.allocate(3); final TestChannelListener<StreamSinkChannel> writeListener = new TestChannelListener<StreamSinkChannel>(); final TestChannelListener<StreamSourceChannel> readListener = new TestChannelListener<StreamSourceChannel>(); sinkChannel.getWriteSetter().set(writeListener); sourceChannel.getReadSetter().set(readListener); assertFalse(sinkChannel.isWriteResumed()); sinkChannel.resumeWrites(); assertFalse(sourceChannel.isReadResumed()); sourceChannel.resumeReads(); assertTrue(writeListener.isInvoked()); // Step 1: shutdownReads on sourceChannel sourceChannel.shutdownReads(); assertFalse(sourceChannel.isReadResumed()); sourceChannel.awaitReadable(); sourceChannel.awaitReadable(10, TimeUnit.SECONDS); assertFalse(sourceChannel.isOpen()); assertTrue(sinkChannel.isOpen()); IOException expected = null; try { sinkChannel.write(buffer); } catch (IOException e) { // broken pipe expected = e; } assertNotNull(expected); buffer.flip(); assertEquals(-1, sourceChannel.read(buffer)); // Step 2: shutdownWrites on sinkChannel sinkChannel.shutdownWrites(); assertFalse(sinkChannel.isWriteResumed()); assertFalse(sinkChannel.isOpen()); sinkChannel.awaitWritable(); sinkChannel.awaitWritable(1, TimeUnit.MINUTES); expected = null; try { sinkChannel.write(buffer); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); assertEquals(-1, sourceChannel.read(ByteBuffer.allocate(5))); // Step 3: close sinkChannel (idempotent) sinkChannel.close(); assertFalse(sinkChannel.isOpen()); assertFalse(sinkChannel.isWriteResumed()); sinkChannel.awaitWritable(); sinkChannel.awaitWritable(1, TimeUnit.MINUTES); buffer.flip(); expected = null; try { sinkChannel.write(buffer); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); assertEquals(-1, sourceChannel.read(buffer)); // Step 4: close sourceChannel (idempotent) assertFalse(sourceChannel.isOpen()); sourceChannel.close(); assertFalse(sourceChannel.isOpen()); assertFalse(sourceChannel.isReadResumed()); sourceChannel.awaitReadable(); sourceChannel.awaitReadable(10, TimeUnit.SECONDS); expected = null; try { sinkChannel.write(buffer); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); assertEquals(-1, sourceChannel.read(buffer)); // Step 5: idempotent shutdown and close operations sinkChannel.shutdownWrites(); sourceChannel.shutdownReads(); sinkChannel.close(); sourceChannel.close(); assertFalse(sinkChannel.isOpen()); assertFalse(sourceChannel.isOpen()); expected = null; try { sinkChannel.write(buffer); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); assertEquals(-1, sourceChannel.read(buffer)); } @Test public void sinkChannelOptionSetup() throws IOException { initChannels(); final Option<?>[] unsupportedOptions = OptionHelper.getNotSupportedOptions(Options.WRITE_TIMEOUT); for (Option<?> option: unsupportedOptions) { assertFalse("Channel supports " + option, sinkChannel.supportsOption(option)); assertNull("Expected null value for option " + option + " but got " + sinkChannel.getOption(option) + " instead", sinkChannel.getOption(option)); } assertTrue(sinkChannel.supportsOption(Options.WRITE_TIMEOUT)); sinkChannel.setOption(Options.WRITE_TIMEOUT, 1301093); assertNull("Unexpected option value: " + sinkChannel.getOption(Options.MAX_INBOUND_MESSAGE_SIZE), sinkChannel.setOption(Options.MAX_INBOUND_MESSAGE_SIZE, 50000));// unsupported assertEquals(1301093, (int) sinkChannel.getOption(Options.WRITE_TIMEOUT)); assertNull(sinkChannel.getOption(Options.MAX_INBOUND_MESSAGE_SIZE));// unsupported assertEquals(1301093, (int) sinkChannel.setOption(Options.WRITE_TIMEOUT, 293265)); assertEquals(293265, (int) sinkChannel.getOption(Options.WRITE_TIMEOUT)); // TODO XNIO-171 test setOption(*, null)? } @Test public void sourceChannelOptionSetup() throws IOException { initChannels(); final Option<?>[] unsupportedOptions = OptionHelper.getNotSupportedOptions(Options.READ_TIMEOUT); for (Option<?> option: unsupportedOptions) { assertFalse("Channel supports " + option, sourceChannel.supportsOption(option)); assertNull("Expected null value for option " + option + " but got " + sourceChannel.getOption(option) + " instead", sourceChannel.getOption(option)); } assertTrue(sourceChannel.supportsOption(Options.READ_TIMEOUT)); sourceChannel.setOption(Options.READ_TIMEOUT, 293265); assertNull("Unexpected option value: " + sourceChannel.getOption(Options.MAX_INBOUND_MESSAGE_SIZE), sourceChannel.setOption(Options.MAX_OUTBOUND_MESSAGE_SIZE, 50000));// unsupported assertEquals(293265, (int) sourceChannel.getOption(Options.READ_TIMEOUT)); assertNull(sourceChannel.getOption(Options.MAX_OUTBOUND_MESSAGE_SIZE));// unsupported assertEquals(293265, (int) sourceChannel.setOption(Options.READ_TIMEOUT, 1301093)); assertEquals(1301093, (int) sourceChannel.getOption(Options.READ_TIMEOUT)); // TODO XNIO-171 test setOption(*, null)? } }