001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.transport.socket.nio; 021 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertNotSame; 024import static org.junit.Assert.assertTrue; 025 026import java.net.InetSocketAddress; 027 028import org.apache.mina.core.buffer.IoBuffer; 029import org.apache.mina.core.future.ConnectFuture; 030import org.apache.mina.core.future.WriteFuture; 031import org.apache.mina.core.service.IoHandlerAdapter; 032import org.apache.mina.core.session.ExpiringSessionRecycler; 033import org.apache.mina.core.session.IdleStatus; 034import org.apache.mina.core.session.IoSession; 035import org.apache.mina.util.AvailablePortFinder; 036import org.junit.After; 037import org.junit.Before; 038import org.junit.Test; 039 040/** 041 * Tests if datagram sessions are recycled properly. 042 * 043 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 044 */ 045public class DatagramRecyclerTest { 046 private NioDatagramAcceptor acceptor; 047 048 private NioDatagramConnector connector; 049 050 public DatagramRecyclerTest() { 051 // Do nothing 052 } 053 054 @Before 055 public void setUp() throws Exception { 056 acceptor = new NioDatagramAcceptor(); 057 connector = new NioDatagramConnector(); 058 } 059 060 @After 061 public void tearDown() throws Exception { 062 acceptor.dispose(); 063 connector.dispose(); 064 } 065 066 @Test 067 public void testDatagramRecycler() throws Exception { 068 int port = AvailablePortFinder.getNextAvailable(1024); 069 ExpiringSessionRecycler recycler = new ExpiringSessionRecycler(1, 1); 070 071 MockHandler acceptorHandler = new MockHandler(); 072 MockHandler connectorHandler = new MockHandler(); 073 074 acceptor.setHandler(acceptorHandler); 075 acceptor.setSessionRecycler(recycler); 076 acceptor.bind(new InetSocketAddress(port)); 077 078 try { 079 connector.setHandler(connectorHandler); 080 ConnectFuture future = connector.connect(new InetSocketAddress("localhost", port)); 081 future.awaitUninterruptibly(); 082 083 // Write whatever to trigger the acceptor. 084 future.getSession().write(IoBuffer.allocate(1)).awaitUninterruptibly(); 085 086 // Close the client-side connection. 087 // This doesn't mean that the acceptor-side connection is also closed. 088 // The life cycle of the acceptor-side connection is managed by the recycler. 089 future.getSession().close(true); 090 future.getSession().getCloseFuture().awaitUninterruptibly(); 091 assertTrue(future.getSession().getCloseFuture().isClosed()); 092 093 // Wait until the acceptor-side connection is closed. 094 while (acceptorHandler.session == null) { 095 Thread.yield(); 096 } 097 acceptorHandler.session.getCloseFuture().awaitUninterruptibly(3000); 098 099 // Is it closed? 100 assertTrue(acceptorHandler.session.getCloseFuture().isClosed()); 101 102 Thread.sleep(1000); 103 104 assertEquals("CROPSECL", connectorHandler.result.toString()); 105 assertEquals("CROPRECL", acceptorHandler.result.toString()); 106 } finally { 107 acceptor.unbind(); 108 } 109 } 110 111 @Test 112 public void testCloseRequest() throws Exception { 113 int port = AvailablePortFinder.getNextAvailable(1024); 114 ExpiringSessionRecycler recycler = new ExpiringSessionRecycler(10, 1); 115 116 MockHandler acceptorHandler = new MockHandler(); 117 MockHandler connectorHandler = new MockHandler(); 118 119 acceptor.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE, 1); 120 acceptor.setHandler(acceptorHandler); 121 acceptor.setSessionRecycler(recycler); 122 acceptor.bind(new InetSocketAddress(port)); 123 124 try { 125 connector.setHandler(connectorHandler); 126 ConnectFuture future = connector.connect(new InetSocketAddress("localhost", port)); 127 future.awaitUninterruptibly(); 128 129 // Write whatever to trigger the acceptor. 130 future.getSession().write(IoBuffer.allocate(1)).awaitUninterruptibly(); 131 132 // Make sure the connection is closed before recycler closes it. 133 while (acceptorHandler.session == null) { 134 Thread.yield(); 135 } 136 acceptorHandler.session.close(true); 137 assertTrue(acceptorHandler.session.getCloseFuture().awaitUninterruptibly(3000)); 138 139 IoSession oldSession = acceptorHandler.session; 140 141 // Wait until all events are processed and clear the state. 142 long startTime = System.currentTimeMillis(); 143 while (acceptorHandler.result.length() < 8) { 144 Thread.yield(); 145 if (System.currentTimeMillis() - startTime > 5000) { 146 throw new Exception(); 147 } 148 } 149 acceptorHandler.result.setLength(0); 150 acceptorHandler.session = null; 151 152 // Write whatever to trigger the acceptor again. 153 WriteFuture wf = future.getSession().write(IoBuffer.allocate(1)).awaitUninterruptibly(); 154 assertTrue(wf.isWritten()); 155 156 // Make sure the connection is closed before recycler closes it. 157 while (acceptorHandler.session == null) { 158 Thread.yield(); 159 } 160 acceptorHandler.session.close(true); 161 assertTrue(acceptorHandler.session.getCloseFuture().awaitUninterruptibly(3000)); 162 163 future.getSession().close(true).awaitUninterruptibly(); 164 165 assertNotSame(oldSession, acceptorHandler.session); 166 } finally { 167 acceptor.unbind(); 168 } 169 } 170 171 private class MockHandler extends IoHandlerAdapter { 172 public volatile IoSession session; 173 174 public final StringBuffer result = new StringBuffer(); 175 176 /** 177 * Default constructor 178 */ 179 public MockHandler() { 180 super(); 181 } 182 183 @Override 184 public void exceptionCaught(IoSession session, Throwable cause) throws Exception { 185 this.session = session; 186 result.append("CA"); 187 } 188 189 @Override 190 public void messageReceived(IoSession session, Object message) throws Exception { 191 this.session = session; 192 result.append("RE"); 193 } 194 195 @Override 196 public void messageSent(IoSession session, Object message) throws Exception { 197 this.session = session; 198 result.append("SE"); 199 } 200 201 @Override 202 public void sessionClosed(IoSession session) throws Exception { 203 this.session = session; 204 result.append("CL"); 205 } 206 207 @Override 208 public void sessionCreated(IoSession session) throws Exception { 209 this.session = session; 210 result.append("CR"); 211 } 212 213 @Override 214 public void sessionIdle(IoSession session, IdleStatus status) throws Exception { 215 this.session = session; 216 result.append("ID"); 217 } 218 219 @Override 220 public void sessionOpened(IoSession session) throws Exception { 221 this.session = session; 222 result.append("OP"); 223 } 224 } 225}