On OS X, the test does not behave the same way: the session is created
and the byte written, but the server does not have any managed session
so the last assertion fails.
To make this test fail as expected, I need to add the following calls
to AbstractPollingIoAcceptor#unbind0
startupAcceptor();
wakeup();
I may have an explanation. In the unregisterHandles() method, the
handle is closed and then there is a call to wakeup(). However, when
the last bound socket is unregistered, the Acceptor#run() method will
simply exit just after the call to unregisterHandles() because
nHandles == 0. So I think the call to wakeup() has no effect at all,
because the acceptor is not running anymore, thus no select() call is
performed.
I've also tried the following patch:
Index:
core/src/main/java/org/apache/mina/core/polling/AbstractPollingIoAcceptor.java
===================================================================
---
core/src/main/java/org/apache/mina/core/polling/AbstractPollingIoAcceptor.java
(revision 747700)
+++
core/src/main/java/org/apache/mina/core/polling/AbstractPollingIoAcceptor.java
(working copy)
@@ -406,6 +406,7 @@
}
// check to see if any cancellation request has been made.
+ int nbPreviousHandles = nHandles;
nHandles -= unregisterHandles();
// Now, if the number of registred handles is 0, we can
@@ -415,6 +416,9 @@
synchronized (lock) {
if (registerQueue.isEmpty()
&& cancelQueue.isEmpty()) {
+ if (nbPreviousHandles > 0) {
+ select();
+ }
acceptor = null;
break;
}
But I don't think it's very clean either.
Note that the problem also happen with datagram sockets and none of
the above solution seems to work in that case.
2009/2/25 Emmanuel Lecharny <[email protected]>:
> Guillaume Nodet wrote:
>>
>> I've just spotted that the acceptor is not correctly disposed.
>> I've managed to get the test running succesfully by adding the
>> following code just after server.suspend()
>>
>> for (Listener listener :
>> server.getServerContext().getListeners().values()) {
>> if (listener instanceof NioListener) {
>> Field field =
>> listener.getClass().getDeclaredField("acceptor");
>> field.setAccessible(true);
>> NioSocketAcceptor acceptor = (NioSocketAcceptor)
>> field.get(listener);
>> Method method =
>> AbstractPollingIoAcceptor.class.getDeclaredMethod("dispose0");
>> method.setAccessible(true);
>> method.invoke(acceptor);
>> }
>> }
>>
>> Which is about callling the dispose0() method of the NioSocketAcceptor.
>> This method looks like:
>>
>> protected IoFuture dispose0() throws Exception {
>> unbind();
>> if (!disposalFuture.isDone()) {
>> startupAcceptor();
>> wakeup();
>> }
>> return disposalFuture;
>> }
>>
>> So I guess what we're missing is a call to:
>> startupAcceptor();
>> wakeup();
>> somewhere.
>> I'm not too familiar with mina internals, so I'm not sure where this
>> methods should be called or if it makes any sense to call those after
>> an unbind.
>>
>
> The problem is that when you do an acceptor.unbind(), here is what MINA does :
>
> acceptor.unbind() -->
> unbind(getLocalAddresses()) -->
> unbind0(localAddressesCopy) with
>
> protected final void unbind0(List<? extends SocketAddress> localAddresses)
> throws Exception {
> AcceptorOperationFuture future = new AcceptorOperationFuture(
> localAddresses);
>
> cancelQueue.add(future);
> startupAcceptor();
> wakeup();
> ...
>
> There is obviously something wrong somewhere, if you consider the following
> test :
> org.apache.mina.transport.AbstractBindTest
>
> public void testUnbindDisconnectsClients() throws Exception {
> bind(true);
> IoConnector connector = newConnector();
> IoSession[] sessions = new IoSession[5];
> connector.setHandler(new IoHandlerAdapter());
>
> // Create 5 sessions, and write when established
> for (int i = 0; i < sessions.length; i++) {
> ConnectFuture future = connector.connect(createSocketAddress(port));
> future.awaitUninterruptibly();
> sessions[i] = future.getSession();
> Assert.assertTrue(sessions[i].isConnected());
>
> Assert.assertTrue(sessions[i].write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
> }
>
> // Wait for the server side sessions to be created.
> Thread.sleep(500);
>
> Collection<IoSession> managedSessions =
> acceptor.getManagedSessions().values();
> Assert.assertEquals(5, managedSessions.size());
>
> // Now unbind
> acceptor.unbind();
>
> // Wait for the accessor to unbind
> Thread.sleep(500);
>
> // The session must have been closed
> Assert.assertEquals(0, managedSessions.size());
> for (IoSession element : managedSessions) {
> Assert.assertFalse(element.isConnected());
> }
>
> // And now, try to create a session on a unbound acceptor !!!
> ConnectFuture future = connector.connect(createSocketAddress(port));
> future.awaitUninterruptibly();
> IoSession session = future.getSession();
> Assert.assertTrue(session.isConnected());
>
> Assert.assertTrue(session.write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
>
> // Wait for the server side sessions to be created.
> Thread.sleep(500);
>
> managedSessions = acceptor.getManagedSessions().values();
> Assert.assertEquals(1, managedSessions.size());
> }
>
>
> On Linux, this tests passes, all lights green !
>
> --
> --
> cordialement, regards,
> Emmanuel Lécharny
> www.iktek.com
> directory.apache.org
>
>
>
--
Cheers,
Guillaume Nodet
------------------------
Blog: http://gnodet.blogspot.com/
------------------------
Open Source SOA
http://fusesource.com