/**
* Copyright (C) 2009-2013 Barchart, Inc. <http://www.barchart.com/>
*
* All rights reserved. Licensed under the OSI BSD License.
*
* http://www.opensource.org/licenses/bsd-license.php
*/
package com.barchart.udt.nio;
import static java.nio.channels.SelectionKey.*;
import static org.junit.Assert.*;
import static util.UnitHelp.*;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Arrays;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import util.TestAny;
/**
* basic single thread selector test
*/
public class TestSelectorUDT extends TestAny {
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
protected static final int SIZE = 1460;
protected static final int COUNT = 1000;
final SelectorProvider provider = SelectorProviderUDT.DATAGRAM;
volatile SelectionKey acceptKey;
volatile SelectionKey serverKey;
volatile SelectionKey clientKey;
volatile ServerSocketChannel acceptChannel;
volatile SocketChannel serverChannel;
volatile SocketChannel clientChannel;
volatile Selector selector;
volatile SocketAddress acceptorAddress;
volatile SocketAddress clientAddress;
@Before
public void init() throws Exception {
selector = provider.openSelector();
acceptChannel = provider.openServerSocketChannel();
acceptChannel.configureBlocking(false);
acceptorAddress = localSocketAddress();
acceptChannel.socket().bind(acceptorAddress);
acceptKey = acceptChannel.register(selector, OP_ACCEPT);
clientChannel = provider.openSocketChannel();
clientChannel.configureBlocking(false);
clientAddress = localSocketAddress();
clientChannel.socket().bind(clientAddress);
clientKey = clientChannel.register(selector, OP_CONNECT);
clientChannel.connect(acceptorAddress);
log.info("test init");
}
@After
public void done() throws Exception {
log.info("test done");
}
volatile boolean isTestON = true;
/** single pass state machine */
/** FIXME */
@Ignore
@Test
public void testSelect() {
try {
final Set<SelectionKey> selectedKeySet = selector.selectedKeys();
selectLoop: while (isTestON) {
final long timeout = 100;
final int readyCount = selector.select(timeout);
if (readyCount == 0) {
continue selectLoop;
}
keyLoop: for (final SelectionKey key : selectedKeySet) {
if (!key.isValid()) {
continue keyLoop;
}
if (key.isReadable()) {
doRead(key);
}
if (key.isWritable()) {
doWrite(key);
}
if (key.isAcceptable()) {
doAccept(key);
}
if (key.isConnectable()) {
doConnect(key);
}
}
selectedKeySet.clear();
}
log.info("before close");
selector.close();
log.info("after close");
} catch (final Exception e) {
fail(e.getMessage());
}
}
interface Handler {
void handleRead();
void handleWrite();
}
final Queue<byte[]> serverQueue = new ConcurrentLinkedQueue<byte[]>();
final Handler serverHandler = new Handler() {
@Override
public String toString() {
return "serverHandler;" + " serverQueue:" + serverQueue.size()
+ " serverChannel:" + serverChannel;
}
final ByteBuffer readerBuffer = ByteBuffer.allocateDirect(SIZE);
@Override
public void handleRead() {
try {
while (true) {
readerBuffer.clear();
final int readSize = serverChannel.read(readerBuffer);
if (readSize <= 0) {
return;
}
assertEquals(readSize, SIZE);
final byte[] array = new byte[readSize];
readerBuffer.flip();
readerBuffer.get(array);
serverQueue.offer(array);
serverKey.interestOps( //
serverKey.interestOps() | OP_WRITE);
}
} catch (final Exception e) {
fail(e.getMessage());
}
}
final ByteBuffer writerBuffer = ByteBuffer.allocateDirect(SIZE);
@Override
public void handleWrite() {
try {
byte[] array;
while ((array = serverQueue.poll()) != null) {
writerBuffer.clear();
writerBuffer.put(array);
writerBuffer.flip();
final int writeSize = serverChannel.write(writerBuffer);
if (writeSize <= 0) {
break;
}
assertEquals(writeSize, SIZE);
}
serverKey.interestOps(serverKey.interestOps() & ~OP_WRITE);
} catch (final Exception e) {
fail(e.getMessage());
}
}
};
final Queue<byte[]> clientQueue = new ConcurrentLinkedQueue<byte[]>();
final Handler clientHandler = new Handler() {
@Override
public String toString() {
return "clientHandler;" + " clientQueue:" + clientQueue.size()
+ " clientChannel:" + clientChannel;
}
final ByteBuffer readerBuffer = ByteBuffer.allocateDirect(SIZE);
final AtomicInteger readCount = new AtomicInteger(0);
@Override
public void handleRead() {
try {
while (true) {
readerBuffer.clear();
final int readSize = clientChannel.read(readerBuffer);
if (readSize <= 0) {
return;
}
assertEquals(readSize, SIZE);
final byte[] arrayRead = new byte[readSize];
readerBuffer.flip();
readerBuffer.get(arrayRead);
final byte[] arrayWritten = clientQueue.poll();
assertNotNull(arrayWritten);
assertTrue(Arrays.equals(arrayRead, arrayWritten));
final int count = readCount.incrementAndGet();
if (count == COUNT) {
clientKey.interestOps( //
clientKey.interestOps() & ~OP_READ);
isTestON = false;
log.info("client read done");
return;
}
}
} catch (final Exception e) {
fail(e.getMessage());
}
}
final ByteBuffer writerBuffer = ByteBuffer.allocateDirect(SIZE);
final Random random = new Random();
final AtomicInteger writeCount = new AtomicInteger(0);
@Override
public void handleWrite() {
try {
while (true) {
final byte[] array = new byte[SIZE];
random.nextBytes(array);
writerBuffer.clear();
writerBuffer.put(array);
writerBuffer.flip();
final int writeSize = clientChannel.write(writerBuffer);
if (writeSize <= 0) {
return;
}
assertEquals(writeSize, SIZE);
clientQueue.offer(array);
final int count = writeCount.incrementAndGet();
if (count == COUNT) {
clientKey.interestOps(//
clientKey.interestOps() & ~OP_WRITE);
log.info("client write done");
return;
}
}
} catch (final Exception e) {
fail(e.getMessage());
}
}
};
private void doAccept(final SelectionKey key) {
try {
log.info("doAccept; key={}", key);
assertEquals(key, acceptKey);
assertEquals(acceptChannel, key.channel());
assertNull(serverChannel);
assertNull(serverKey);
serverChannel = acceptChannel.accept();
serverChannel.configureBlocking(false);
serverKey = serverChannel.register(selector, OP_READ);
serverKey.attach(serverHandler);
} catch (final Exception e) {
fail(e.getMessage());
}
}
private void doConnect(final SelectionKey key) {
try {
log.info("doConnect; key={}", key);
assertEquals(key, clientKey);
assertEquals(clientChannel, key.channel());
assertTrue(clientChannel.finishConnect());
assertTrue(clientChannel.isConnected());
clientKey.interestOps(OP_READ | OP_WRITE);
clientKey.attach(clientHandler);
} catch (final Exception e) {
fail(e.getMessage());
}
}
private void doRead(final SelectionKey key) {
log.info("key:\n\t{}", key);
final Object attachment = key.attachment();
assertTrue(attachment instanceof Handler);
final Handler handler = (Handler) attachment;
handler.handleRead();
}
private void doWrite(final SelectionKey key) {
log.info("key:\n\t{}", key);
final Object attachment = key.attachment();
assertTrue(attachment instanceof Handler);
final Handler handler = (Handler) attachment;
handler.handleWrite();
}
@Test(timeout = 3000)
public void testWakeup() throws Exception {
final SelectorProvider provider = SelectorProviderUDT.DATAGRAM;
final Selector selector = provider.openSelector();
final Thread thread = new Thread() {
@Override
public void run() {
try {
log.info("init");
/** blocking */
selector.select();
log.info("done");
} catch (final IOException e) {
e.printStackTrace();
}
}
};
thread.start();
Thread.sleep(300);
selector.wakeup();
thread.join();
}
}