FULL PRODUCT VERSION :
java version "1.5.0_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_04-b05)
Java HotSpot(TM) Client VM (build 1.5.0_04-b05, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195]
A DESCRIPTION OF THE PROBLEM :
code reproducing problem is attached. rebind does not work in this JUnit test case I believe due to close happening when in the selector. Something is not shutting down and cleaning up the socket properly in the JVM I beleive.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run my unit test. I wrote this specifically just to test and reproduce this issue.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
test case to pass. I should be able to rebind the socket since this line is in the code before the first bind
client.socket().setReuseAddress(true);
and the this line occurs before the second bind
client.close();
This works in most cases but just not in this specific test case.
ACTUAL -
test case fails. BindException on the second bind after closing the first client.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
BindException: address is already bound
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
/*
* Created on Sep 25, 2004
*
* FIXME To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package biz.xsoftware.test.nio.suns;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
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 java.util.logging.Logger;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.textui.TestRunner;
/**
* @author Dean Hiller
*
* FIXME To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class TestCloseWhenInSelector extends TestCase {
private static final Logger log = Logger
.getLogger(TestCloseWhenInSelector.class.getName());
private final static int CLIENT_PORT = 8023;
// private final static int CLIENT_PORT2 = 8002;
private final static int SERVER_PORT = 8020;
private SelectorProvider provider;
private AbstractSelector selector;
private ServerSocketChannel serverSocket;
// private SocketChannel serverChannel;
private SocketChannel client;
// private SocketChannel client2;
private ByteBuffer buf = ByteBuffer.allocate(10);
/**
* @param arg0
*/
public TestCloseWhenInSelector(String arg0) {
super(arg0);
}
protected void setUp() throws Exception {
provider = SelectorProvider.provider();
selector = provider.openSelector();
serverSocket = provider.openServerSocketChannel();
serverSocket.socket().setReuseAddress(true);
client = provider.openSocketChannel();
client.socket().setReuseAddress(true);
}
protected void tearDown() throws Exception {
}
public void testCloseWhenInSelector2() throws Throwable {
InetAddress loopBack = InetAddress.getByName("127.0.0.1");
InetSocketAddress clientAddr = new InetSocketAddress(loopBack, CLIENT_PORT);
InetSocketAddress serverAddr = new InetSocketAddress(loopBack, SERVER_PORT);
//serverSocket.configureBlocking(false);
serverSocket.socket().bind(serverAddr);
client.socket().bind(clientAddr);
client.connect(serverAddr);
client.configureBlocking(false);
log.info("connecting client socket");
Thread.sleep(1000);
serverSocket.configureBlocking(true);
log.info("about to accept");
SocketChannel serverChannel = serverSocket.accept();
log.info("accepted");
serverChannel.configureBlocking(false);
log.info("client socket connected");
serverChannel.register(selector, SelectionKey.OP_READ);
PollingThread2 server = new PollingThread2();
server.start();
client.finishConnect();
log.info("write data to server");
ByteBuffer b = ByteBuffer.allocate(10);
b.putChar('d');
b.putChar('e');
b.flip();
log.info("write bytes");
int i = client.write(b);
log.info("wrote bytes="+i);
//wait for other thread to get into the selector...
Thread.sleep(1000);
log.info("1. closing client channel");
client.close();
log.info("1. closed client channel");
server.waitForCompletion();
client = provider.openSocketChannel();
client.socket().setReuseAddress(true);
client.socket().bind(clientAddr);
client.connect(serverAddr);
client.configureBlocking(false);
}
private class PollingThread2 extends Thread {
private Throwable t = null;
private boolean socketClosed = false;
public void run() {
try {
log.info("STARTING RUN LOOP");
while(true) {
runLoop();
}
} catch (Exception e) {
t = e;
log.log(Level.WARNING, "Test failure", e);
}
}
/**
*
*/
synchronized public void waitForCompletion() throws Throwable {
if(!socketClosed)
this.wait();
if(t != null)
throw t;
}
protected void runLoop() throws Exception {
log.info("going into selector");
int numKeys = selector.select();
log.info("coming out with new keys="+numKeys);
Set<SelectionKey> keySet = selector.selectedKeys();
log.info("keySet size="+keySet.size());
Iterator<SelectionKey> iter = keySet.iterator();
SelectionKey theKey = iter.next();
log.info("in loop iter.next="+theKey+" isVal="+theKey.isValid()+" acc="+theKey.isAcceptable()+" read="+theKey.isReadable());
if(theKey.isReadable()) {
SocketChannel channel = (SocketChannel)theKey.channel();
log.info("reading bytes");
int b = 5;
while(b > 0) {
b = channel.read(buf);
if(b < 0) {
channel.close();
synchronized(this) {
socketClosed = true;
this.notifyAll();
}
}
log.info("bytes read="+b);
}
}
if(iter.hasNext())
throw new RuntimeException("Fail test, iterator should only have one key");
}
}
public static void main(String[] args) {
TestSuite suite = new TestSuite();
suite.addTest(new TestCloseWhenInSelector("testCloseWhenInSelector2"));
TestRunner.run(suite);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
I believe it is to pray that close is not called when in the selector or something....not sure the specifics of it. not really a work around here.