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.transport.nio;
21  
22  import java.io.IOException;
23  import java.net.SocketAddress;
24  import java.net.SocketException;
25  import java.nio.channels.SelectionKey;
26  import java.nio.channels.SocketChannel;
27  
28  import org.apache.mina.api.IdleStatus;
29  import org.apache.mina.api.IoFuture;
30  import org.apache.mina.api.IoSession;
31  import org.apache.mina.api.MinaRuntimeException;
32  import org.apache.mina.service.executor.IoHandlerExecutor;
33  import org.apache.mina.service.executor.OrderedHandlerExecutor;
34  import org.apache.mina.service.idlechecker.IdleChecker;
35  import org.apache.mina.service.idlechecker.IndexedIdleChecker;
36  import org.apache.mina.transport.ConnectFuture;
37  import org.apache.mina.transport.tcp.AbstractTcpClient;
38  import org.apache.mina.transport.tcp.TcpSessionConfig;
39  import org.apache.mina.util.Assert;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  /**
44   * This class implements a TCP NIO based client.
45   * 
46   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
47   */
48  public class NioTcpClient extends AbstractTcpClient {
49  
50      /** A logger for this class */
51      private static final Logger LOG = LoggerFactory.getLogger(NioTcpClient.class);
52  
53      /** the SelectorLoop for connecting the sessions */
54      // This is final, so that we know if it's not initialized
55      private final SelectorLoop connectSelectorLoop;
56  
57      /** the Selectorloop for handling read/write session events */
58      // This is final, so that we know if it's not initialized
59      private final SelectorLoopPool readWriteSelectorPool;
60  
61      /** for detecting idle session */
62      private IdleChecker idleChecker;
63  
64      /**
65       * Create a TCP client with new selector pool of default size and a {@link IoHandlerExecutor} of default type (
66       * {@link OrderedHandlerExecutor})
67       */
68      public NioTcpClient() {
69          // Default to 2 threads in the pool
70          this(new NioSelectorLoop("connect", 0), new FixedSelectorLoopPool("Client", 2), null);
71      }
72  
73      /**
74       * Create a TCP client with provided selector loops pool. We will use one SelectorLoop get from the pool to manage
75       * the OP_CONNECT events. If the pool contains only one SelectorLoop, then all the events will be managed by the
76       * same Selector.
77       * 
78       * @param readWriteSelectorLoop the pool of selector loop for handling read/write events of connected sessions
79       * @param ioHandlerExecutor used for executing IoHandler event in another pool of thread (not in the low level I/O
80       *        one). Use <code>null</code> if you don't want one. Be careful, the IoHandler processing will block the I/O
81       *        operations.
82       */
83      public NioTcpClient(SelectorLoopPool selectorLoopPool, IoHandlerExecutor handlerExecutor) {
84          super(handlerExecutor);
85          connectSelectorLoop = selectorLoopPool.getSelectorLoop();
86          readWriteSelectorPool = selectorLoopPool;
87      }
88  
89      /**
90       * Create a TCP client with provided selector loops pool
91       * 
92       * @param connectSelectorLoop the selector loop for handling connection events (connection of new session)
93       * @param readWriteSelectorLoop the pool of selector loop for handling read/write events of connected sessions
94       * @param ioHandlerExecutor used for executing IoHandler event in another pool of thread (not in the low level I/O
95       *        one). Use <code>null</code> if you don't want one. Be careful, the IoHandler processing will block the I/O
96       *        operations.
97       */
98      public NioTcpClient(SelectorLoop connectSelectorLoop, SelectorLoopPool readWriteSelectorLoop,
99              IoHandlerExecutor handlerExecutor) {
100         super(handlerExecutor);
101         this.connectSelectorLoop = connectSelectorLoop;
102         this.readWriteSelectorPool = readWriteSelectorLoop;
103         idleChecker = new IndexedIdleChecker();
104     }
105 
106     /**
107      * {@inheritDoc}
108      */
109     @Override
110     public IoFuture<IoSession> connect(SocketAddress remoteAddress) {
111         Assert.assertNotNull(remoteAddress, "remoteAddress");
112 
113         SocketChannel clientSocket;
114         try {
115             clientSocket = SocketChannel.open();
116         } catch (IOException e) {
117             throw new MinaRuntimeException("can't create a new socket, out of file descriptors ?", e);
118         }
119 
120         try {
121             clientSocket.socket().setSoTimeout(getConnectTimeoutMillis());
122         } catch (SocketException e) {
123             throw new MinaRuntimeException("can't set socket timeout", e);
124         }
125 
126         // non blocking
127         try {
128             clientSocket.configureBlocking(false);
129         } catch (IOException e) {
130             throw new MinaRuntimeException("can't configure socket as non-blocking", e);
131         }
132 
133         // apply idle configuration
134         // Has to be final, as it's used in a inner class...
135         final NioTcpSession session = new NioTcpSession(this, clientSocket, readWriteSelectorPool.getSelectorLoop(),
136                 idleChecker);
137         TcpSessionConfig config = getSessionConfig();
138 
139         session.getConfig().setIdleTimeInMillis(IdleStatus.READ_IDLE, config.getIdleTimeInMillis(IdleStatus.READ_IDLE));
140         session.getConfig().setIdleTimeInMillis(IdleStatus.WRITE_IDLE,
141                 config.getIdleTimeInMillis(IdleStatus.WRITE_IDLE));
142 
143         // apply the default service socket configuration
144         Boolean keepAlive = config.isKeepAlive();
145 
146         if (keepAlive != null) {
147             session.getConfig().setKeepAlive(keepAlive);
148         }
149 
150         Boolean oobInline = config.isOobInline();
151 
152         if (oobInline != null) {
153             session.getConfig().setOobInline(oobInline);
154         }
155 
156         Boolean reuseAddress = config.isReuseAddress();
157 
158         if (reuseAddress != null) {
159             session.getConfig().setReuseAddress(reuseAddress);
160         }
161 
162         Boolean tcpNoDelay = config.isTcpNoDelay();
163 
164         if (tcpNoDelay != null) {
165             session.getConfig().setTcpNoDelay(tcpNoDelay);
166         }
167 
168         Integer receiveBufferSize = config.getReadBufferSize();
169 
170         if (receiveBufferSize != null) {
171             session.getConfig().setReadBufferSize(receiveBufferSize);
172         } else {
173             int rcvBufferSize;
174             try {
175                 rcvBufferSize = clientSocket.socket().getReceiveBufferSize();
176             } catch (SocketException e) {
177                 throw new MinaRuntimeException("can't configure socket receive buffer size", e);
178             }
179             session.getConfig().setReadBufferSize(rcvBufferSize);
180         }
181 
182         Integer sendBufferSize = config.getSendBufferSize();
183 
184         if (sendBufferSize != null) {
185             session.getConfig().setSendBufferSize(sendBufferSize);
186         } else {
187             int sndBufferSize;
188             try {
189                 sndBufferSize = clientSocket.socket().getSendBufferSize();
190             } catch (SocketException e) {
191                 throw new MinaRuntimeException("can't configure socket send buffe size", e);
192             }
193             session.getConfig().setSendBufferSize(sndBufferSize);
194         }
195 
196         Integer trafficClass = config.getTrafficClass();
197 
198         if (trafficClass != null) {
199             session.getConfig().setTrafficClass(trafficClass);
200         }
201 
202         Integer soLinger = config.getSoLinger();
203 
204         if (soLinger != null) {
205             session.getConfig().setSoLinger(soLinger);
206         }
207 
208         // Set the secured flag if the service is to be used over SSL/TLS
209         if (config.isSecured()) {
210             session.initSecure(config.getSslContext());
211         }
212 
213         // connect to a running server. We get an immediate result if
214         // the socket is blocking, and either true or false if it's non blocking
215         boolean connected;
216         try {
217             connected = clientSocket.connect(remoteAddress);
218         } catch (IOException e) {
219             ConnectFuture future = new ConnectFuture();
220             future.cannotConnect(e);
221             return future;
222         }
223 
224         ConnectFuture connectFuture = new ConnectFuture();
225         session.setConnectFuture(connectFuture);
226 
227         if (!connected) {
228             // async connection, let's the connection complete in background, the selector loop will detect when the
229             // connection is successful
230             connectSelectorLoop.register(false, true, false, false, session, clientSocket, new RegistrationCallback() {
231 
232                 @Override
233                 public void done(SelectionKey selectionKey) {
234                     session.setSelectionKey(selectionKey);
235                 }
236             });
237         } else {
238             // already connected (probably a loopback connection, or a blocking socket)
239             // register for read
240             connectSelectorLoop.register(false, false, true, false, session, clientSocket, new RegistrationCallback() {
241 
242                 @Override
243                 public void done(SelectionKey selectionKey) {
244                     session.setSelectionKey(selectionKey);
245                 }
246             });
247 
248             session.setConnected();
249         }
250 
251         return connectFuture;
252     }
253 
254     /**
255      * {@inheritDoc}
256      */
257     public synchronized void disconnect() throws IOException {
258         LOG.info("Disconnecting sessions");
259 
260         // Close all the existing sessions
261         for (IoSession session : getManagedSessions().values()) {
262             session.close(true);
263         }
264 
265         fireServiceInactivated();
266 
267         // will stop the idle processor if we are the last service
268         idleChecker.destroy();
269     }
270 }