package org.webpieces.nio.test.suns;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.AbstractSelector;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import org.webpieces.util.logging.Logger;
import junit.framework.TestCase;
/**
* Not a bug, just demonstrating behavior now.
*
* Tests that two connects results in one key, not two. This is fine. The
* selector will refire immediately on next call. Also once the two connects
* are accepted(using accept() method on serversocket), the selector blocks
* until the next connect request. See the test below.
*
* The return value of selector.select is the number of keys whose ready
* operations where updated whereas the number of keys in the selectedKey
* set may be larger because the ready state did not change...they were
* ready previously and are still ready and have not added new ready
* operations.
*/
public class TestXDemoMultipleNioConnects extends TestCase {
private static final Logger log = LoggerFactory.getLogger(TestXDemoMultipleNioConnects.class
.getName());
private SelectorProvider provider;
private AbstractSelector selector;
private ServerSocketChannel serverChannel;
private SocketChannel client;
private SocketChannel client2;
private PollingThread server;
protected void setUp() throws Exception {
provider = SelectorProvider.provider();
selector = provider.openSelector();
serverChannel = provider.openServerSocketChannel();
client = provider.openSocketChannel();
client2 = provider.openSocketChannel();
server = new PollingThread();
}
protected void tearDown() throws Exception {
server.shutdown();
}
public void testConnectsInNIO() throws Throwable {
InetAddress loopBack = InetAddress.getByName("127.0.0.1");
InetSocketAddress serverAddr = new InetSocketAddress(loopBack, 0);
InetSocketAddress client1Addr = new InetSocketAddress(loopBack, 0);
InetSocketAddress client2Addr = new InetSocketAddress(loopBack, 0);
serverChannel.configureBlocking(false);
serverChannel.socket().bind(serverAddr);
client.socket().bind(client1Addr);
client2.socket().bind(client2Addr);
SocketAddress addr = serverChannel.socket().getLocalSocketAddress();
log.info("server="+addr);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
//start the thread that sleeps, and then blocks on select
//after the below two socket connects happen
server.start();
client.connect(addr);
client2.connect(addr);
log.info("connected both client sockets");
server.waitForBothSocketAccepts();
log.info("done with test");
}
// private boolean serverChannelCreated = false;
private class PollingThread extends Thread {
private Throwable t = null;
private SocketChannel[] clientChannels = new SocketChannel[2];
private boolean acceptedBothSockets = false;
private boolean shutdown = false;
public void run() {
try {
//sleep so both connect requests have already went through.
Thread.sleep(2000);
log.info("STARTING RUN LOOP");
for(int i = 0; true; i++) {
runLoop(i);
if(shutdown)
break;
}
} catch (Exception e) {
log.error("test failure", e);
t = e;
}
}
/**
*
*/
public synchronized void shutdown() {
log.info("shutting down server");
shutdown = true;
this.interrupt();
}
/**
* @throws Throwable
*
*/
public synchronized void waitForBothSocketAccepts() throws Throwable {
if(!acceptedBothSockets)
this.wait();
//sleep further to make sure the select doesn't keep going off as
//we have processed everything.
Thread.sleep(5000);
if(t != null)
throw t; //to fail the test case if exception happens on polling thread.
assertNotNull("clientChannel 1 should be non null", clientChannels[0]);
assertNotNull("clientChannel 2 should be non null", clientChannels[1]);
assertTrue("clientChannel 1 and 2 should be different", clientChannels[0] != clientChannels[1]);
}
protected void runLoop(int i) throws Exception{
log.info("going into selector");
int numKeys = selector.select();
if(i > 1 && numKeys > 0)
throw new RuntimeException("Failure as we should not pop out of " +
"the selector when i > 1 except on shutdown which should have 0 keys");
if(shutdown)
return;
log.info("coming out with keys="+numKeys);
Set<SelectionKey> keySet = selector.selectedKeys();
log.info("keySet size="+keySet.size());
Iterator<SelectionKey> iter = keySet.iterator();
SelectionKey theKey = iter.next();
log.info(i+": in loop iter.next="+theKey+" isVal="+theKey.isValid()+
" acc="+theKey.isAcceptable()+" read="+theKey.isReadable());
if(theKey.isAcceptable()) {
clientChannels[i] = serverChannel.accept();
synchronized(this) {
if(i >= 1) {
acceptedBothSockets = true;
this.notifyAll();
}
}
}
if(iter.hasNext())
throw new RuntimeException("Should only have one key");
log.info(i+":first channel="+clientChannels[i]);
}
}
}