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: */
020: package org.apache.mina.transport;
021:
022: import java.io.IOException;
023: import java.net.SocketAddress;
024: import java.util.Collection;
025: import java.util.Date;
026:
027: import junit.framework.Assert;
028: import junit.framework.TestCase;
029:
030: import org.apache.mina.common.ConnectFuture;
031: import org.apache.mina.common.IdleStatus;
032: import org.apache.mina.common.IoAcceptor;
033: import org.apache.mina.common.IoBuffer;
034: import org.apache.mina.common.IoConnector;
035: import org.apache.mina.common.IoHandlerAdapter;
036: import org.apache.mina.common.IoSession;
037: import org.apache.mina.transport.socket.DatagramAcceptor;
038: import org.apache.mina.transport.socket.DatagramSessionConfig;
039: import org.apache.mina.transport.socket.SocketAcceptor;
040: import org.apache.mina.transport.socket.SocketSessionConfig;
041: import org.slf4j.Logger;
042: import org.slf4j.LoggerFactory;
043:
044: /**
045: * Tests {@link IoAcceptor} resource leakage by repeating bind and unbind.
046: *
047: * @author The Apache MINA Project (dev@mina.apache.org)
048: * @version $Rev: 600461 $, $Date: 2007-12-03 02:55:52 -0700 (Mon, 03 Dec 2007) $
049: */
050: public abstract class AbstractBindTest extends TestCase {
051: protected final IoAcceptor acceptor;
052:
053: protected int port;
054:
055: public AbstractBindTest(IoAcceptor acceptor) {
056: this .acceptor = acceptor;
057: }
058:
059: protected abstract SocketAddress createSocketAddress(int port);
060:
061: protected abstract int getPort(SocketAddress address);
062:
063: protected abstract IoConnector newConnector();
064:
065: protected void bind(boolean reuseAddress) throws IOException {
066: acceptor.setHandler(new EchoProtocolHandler());
067:
068: setReuseAddress(reuseAddress);
069:
070: // Find an available test port and bind to it.
071: boolean socketBound = false;
072:
073: // Let's start from port #1 to detect possible resource leak
074: // because test will fail in port 1-1023 if user run this test
075: // as a normal user.
076: for (port = 1; port <= 65535; port++) {
077: socketBound = false;
078: try {
079: acceptor
080: .setDefaultLocalAddress(createSocketAddress(port));
081: acceptor.bind();
082: socketBound = true;
083: break;
084: } catch (IOException e) {
085: //System.out.println(e.getMessage());
086: }
087: }
088:
089: // If there is no port available, test fails.
090: if (!socketBound) {
091: throw new IOException("Cannot bind any test port.");
092: }
093:
094: //System.out.println( "Using port " + port + " for testing." );
095: }
096:
097: private void setReuseAddress(boolean reuseAddress) {
098: if (acceptor instanceof DatagramAcceptor) {
099: ((DatagramSessionConfig) acceptor.getSessionConfig())
100: .setReuseAddress(reuseAddress);
101: } else if (acceptor instanceof SocketAcceptor) {
102: ((SocketAcceptor) acceptor).setReuseAddress(reuseAddress);
103: }
104: }
105:
106: @Override
107: public void tearDown() {
108: try {
109: acceptor.dispose();
110: } catch (Exception e) {
111: // ignore
112: }
113:
114: acceptor.setDefaultLocalAddress(null);
115: }
116:
117: public void testAnonymousBind() throws Exception {
118: acceptor.setHandler(new IoHandlerAdapter());
119: acceptor.setDefaultLocalAddress(null);
120: acceptor.bind();
121: Assert.assertNotNull(acceptor.getLocalAddress());
122: acceptor.unbind(acceptor.getLocalAddress());
123: Assert.assertNull(acceptor.getLocalAddress());
124: acceptor.setDefaultLocalAddress(createSocketAddress(0));
125: acceptor.bind();
126: Assert.assertNotNull(acceptor.getLocalAddress());
127: Assert.assertTrue(getPort(acceptor.getLocalAddress()) != 0);
128: acceptor.unbind(acceptor.getLocalAddress());
129: }
130:
131: public void testDuplicateBind() throws IOException {
132: bind(false);
133:
134: try {
135: acceptor.bind();
136: Assert.fail("Exception is not thrown");
137: } catch (Exception e) {
138: }
139: }
140:
141: public void testDuplicateUnbind() throws IOException {
142: bind(false);
143:
144: // this should succeed
145: acceptor.unbind();
146:
147: // this shouldn't fail
148: acceptor.unbind();
149: }
150:
151: public void testManyTimes() throws IOException {
152: bind(true);
153:
154: for (int i = 0; i < 1024; i++) {
155: acceptor.unbind();
156: acceptor.bind();
157: }
158: }
159:
160: public void testUnbindDisconnectsClients() throws Exception {
161: bind(true);
162: IoConnector connector = newConnector();
163: IoSession[] sessions = new IoSession[5];
164: connector.setHandler(new IoHandlerAdapter());
165: for (int i = 0; i < sessions.length; i++) {
166: ConnectFuture future = connector
167: .connect(createSocketAddress(port));
168: future.awaitUninterruptibly();
169: sessions[i] = future.getSession();
170: Assert.assertTrue(sessions[i].isConnected());
171: Assert.assertTrue(sessions[i].write(IoBuffer.allocate(1))
172: .awaitUninterruptibly().isWritten());
173: }
174:
175: // Wait for the server side sessions to be created.
176: Thread.sleep(500);
177:
178: Collection<IoSession> managedSessions = acceptor
179: .getManagedSessions();
180: Assert.assertEquals(5, managedSessions.size());
181:
182: acceptor.unbind();
183:
184: // Wait for the client side sessions to close.
185: Thread.sleep(500);
186:
187: Assert.assertEquals(0, managedSessions.size());
188: for (IoSession element : managedSessions) {
189: Assert.assertFalse(element.isConnected());
190: }
191: }
192:
193: public void _testRegressively() throws IOException {
194: setReuseAddress(true);
195:
196: SocketAddress addr = createSocketAddress(port);
197: EchoProtocolHandler handler = new EchoProtocolHandler();
198: acceptor.setDefaultLocalAddress(addr);
199: acceptor.setHandler(handler);
200: for (int i = 0; i < 1048576; i++) {
201: acceptor.bind();
202: acceptor.unbind();
203: if (i % 100 == 0) {
204: System.out.println(i + " (" + new Date() + ")");
205: }
206: }
207: bind(false);
208: }
209:
210: private static class EchoProtocolHandler extends IoHandlerAdapter {
211: private static final Logger log = LoggerFactory
212: .getLogger(EchoProtocolHandler.class);
213:
214: @Override
215: public void sessionCreated(IoSession session) {
216: if (session.getConfig() instanceof SocketSessionConfig) {
217: ((SocketSessionConfig) session.getConfig())
218: .setReceiveBufferSize(2048);
219: }
220:
221: session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
222: }
223:
224: @Override
225: public void sessionIdle(IoSession session, IdleStatus status) {
226: log.info("*** IDLE #"
227: + session.getIdleCount(IdleStatus.BOTH_IDLE)
228: + " ***");
229: }
230:
231: @Override
232: public void exceptionCaught(IoSession session, Throwable cause) {
233: cause.printStackTrace();
234: session.close();
235: }
236:
237: @Override
238: public void messageReceived(IoSession session, Object message)
239: throws Exception {
240: if (!(message instanceof IoBuffer)) {
241: return;
242: }
243:
244: IoBuffer rb = (IoBuffer) message;
245: // Write the received data back to remote peer
246: IoBuffer wb = IoBuffer.allocate(rb.remaining());
247: wb.put(rb);
248: wb.flip();
249: session.write(wb);
250: }
251: }
252: }
|