View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.example.echoserver;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.fail;
24  
25  import java.net.InetAddress;
26  import java.net.InetSocketAddress;
27  
28  import org.apache.mina.core.buffer.IoBuffer;
29  import org.apache.mina.core.future.ConnectFuture;
30  import org.apache.mina.core.future.WriteFuture;
31  import org.apache.mina.core.service.IoConnector;
32  import org.apache.mina.core.service.IoHandlerAdapter;
33  import org.apache.mina.core.session.IoSession;
34  import org.apache.mina.core.write.WriteException;
35  import org.apache.mina.example.echoserver.ssl.BogusSslContextFactory;
36  import org.apache.mina.filter.ssl.SslFilter;
37  import org.apache.mina.transport.socket.nio.NioDatagramConnector;
38  import org.apache.mina.transport.socket.nio.NioSocketConnector;
39  import org.apache.mina.util.AvailablePortFinder;
40  import org.junit.Before;
41  import org.junit.Test;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  /**
46   * Tests echo server example.
47   *
48   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
49   */
50  public class ConnectorTest extends AbstractTest {
51      private final static Logger LOGGER = LoggerFactory.getLogger(ConnectorTest.class);
52  
53      private static final int TIMEOUT = 10000; // 10 seconds
54  
55      private final int COUNT = 10;
56  
57      private final int DATA_SIZE = 16;
58  
59      private EchoConnectorHandler handler;
60      private SslFilter connectorSSLFilter;
61  
62      public ConnectorTest() {
63          // Do nothing
64      }
65  
66      @Before
67      public void setUp() throws Exception {
68          super.setUp();
69          handler = new EchoConnectorHandler();
70          connectorSSLFilter = new SslFilter(BogusSslContextFactory
71                  .getInstance(false));
72          connectorSSLFilter.setUseClientMode(true); // set client mode
73      }
74  
75      @Test
76      public void testTCP() throws Exception {
77          IoConnector connector = new NioSocketConnector();
78          testConnector(connector);
79      }
80  
81      @Test
82      public void testTCPWithSSL() throws Exception {
83          useSSL = true;
84          // Create a connector
85          IoConnector connector = new NioSocketConnector();
86  
87          // Add an SSL filter to connector
88          connector.getFilterChain().addLast("SSL", connectorSSLFilter);
89          testConnector(connector);
90      }
91  
92      @Test
93      public void testUDP() throws Exception {
94          IoConnector connector = new NioDatagramConnector();
95          testConnector(connector);
96      }
97  
98      private void testConnector(IoConnector connector) throws Exception {
99          connector.setHandler(handler);
100 
101         //System.out.println("* Without localAddress");
102         testConnector(connector, false);
103 
104         //System.out.println("* With localAddress");
105         testConnector(connector, true);
106     }
107 
108     private void testConnector(IoConnector connector, boolean useLocalAddress)
109             throws Exception {
110         IoSession session = null;
111         
112         if (!useLocalAddress) {
113             ConnectFuture future = connector.connect(new InetSocketAddress(
114                 InetAddress.getByName(null), port));
115             future.awaitUninterruptibly();
116             session = future.getSession();
117         } else {
118             int clientPort = AvailablePortFinder.getNextAvailable();
119             ConnectFuture future = connector.connect(
120                     new InetSocketAddress(InetAddress.getByName(null), port),
121                     new InetSocketAddress(clientPort));
122             future.awaitUninterruptibly();
123             session = future.getSession();
124 
125             if (session == null) {
126                 fail("Failed to find out an appropriate local address.");
127             }
128         }
129 
130         // Run a basic connector test.
131         testConnector0(session);
132 
133         // Send closeNotify to test TLS closure if it is TLS connection.
134         if (useSSL) {
135             session.getFilterChain().remove("SSL");
136         }
137 
138         session.closeNow().awaitUninterruptibly();
139     }
140 
141     private void testConnector0(IoSession session) throws InterruptedException {
142         EchoConnectorHandler handler = (EchoConnectorHandler) session
143                 .getHandler();
144         IoBuffer readBuf = handler.readBuf;
145         readBuf.clear();
146         WriteFuture writeFuture = null;
147         
148         for (int i = 0; i < COUNT; i++) {
149             IoBuffer buf = IoBuffer.allocate(DATA_SIZE);
150             buf.limit(DATA_SIZE);
151             fillWriteBuffer(buf, i);
152             buf.flip();
153 
154             writeFuture = session.write(buf);
155 
156             if (session.getService().getTransportMetadata().isConnectionless()) {
157                 // This will align message arrival order in connectionless transport types
158                 waitForResponse(handler, (i + 1) * DATA_SIZE);
159             }
160         }
161 
162         writeFuture.awaitUninterruptibly();
163 
164         waitForResponse(handler, DATA_SIZE * COUNT);
165 
166         // Assert data
167         //// Please note that BufferOverflowException can be thrown
168         //// in SocketIoProcessor if there was a read timeout because
169         //// we share readBuf.
170         readBuf.flip();
171         LOGGER.info("readBuf: " + readBuf);
172         assertEquals(DATA_SIZE * COUNT, readBuf.remaining());
173         IoBuffer expectedBuf = IoBuffer.allocate(DATA_SIZE * COUNT);
174         
175         for (int i = 0; i < COUNT; i++) {
176             expectedBuf.limit((i + 1) * DATA_SIZE);
177             fillWriteBuffer(expectedBuf, i);
178         }
179         
180         expectedBuf.position(0);
181 
182         isEquals(expectedBuf, readBuf);
183     }
184 
185     private void waitForResponse(EchoConnectorHandler handler, int bytes)
186             throws InterruptedException {
187         for (int j = 0; j < TIMEOUT / 10; j++) {
188             if (handler.readBuf.position() >= bytes) {
189                 break;
190             }
191             Thread.sleep(10);
192         }
193 
194         assertEquals(bytes, handler.readBuf.position());
195     }
196 
197     private void fillWriteBuffer(IoBuffer writeBuf, int i) {
198         while (writeBuf.remaining() > 0) {
199             writeBuf.put((byte) i++);
200         }
201     }
202 
203     private static class EchoConnectorHandler extends IoHandlerAdapter {
204         private final IoBuffer readBuf = IoBuffer.allocate(1024);
205 
206         private EchoConnectorHandler() {
207             readBuf.setAutoExpand(true);
208         }
209 
210         @Override
211         public void messageReceived(IoSession session, Object message) {
212             readBuf.put((IoBuffer) message);
213         }
214 
215         @Override
216         public void messageSent(IoSession session, Object message) {
217         }
218 
219         @Override
220         public void exceptionCaught(IoSession session, Throwable cause) {
221             LOGGER.warn("Unexpected exception.", cause);
222             if (cause instanceof WriteException) {
223                 WriteException e = (WriteException) cause;
224                 LOGGER.warn("Failed write requests: {}", e.getRequests());
225             }
226         }
227     }
228 }