View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport.tcp;
8   
9   import org.mule.api.MessagingException;
10  import org.mule.api.MuleContext;
11  import org.mule.api.MuleException;
12  import org.mule.api.MuleMessage;
13  import org.mule.api.endpoint.ImmutableEndpoint;
14  import org.mule.api.lifecycle.InitialisationException;
15  import org.mule.api.transport.Connector;
16  import org.mule.api.transport.MessageDispatcherFactory;
17  import org.mule.config.i18n.CoreMessages;
18  import org.mule.model.streaming.CallbackOutputStream;
19  import org.mule.transport.AbstractConnector;
20  import org.mule.transport.ConfigurableKeyedObjectPool;
21  import org.mule.transport.tcp.protocols.SafeProtocol;
22  import org.mule.util.concurrent.ThreadNameHelper;
23  import org.mule.util.monitor.ExpiryMonitor;
24  
25  import java.io.BufferedOutputStream;
26  import java.io.DataOutputStream;
27  import java.io.IOException;
28  import java.io.OutputStream;
29  import java.net.ServerSocket;
30  import java.net.Socket;
31  import java.net.SocketException;
32  import java.net.URI;
33  
34  import org.apache.commons.pool.impl.GenericKeyedObjectPool;
35  
36  /**
37   * <code>TcpConnector</code> can bind or sent to a given TCP port on a given host.
38   * Other socket-based transports can be built on top of this class by providing the
39   * appropriate socket factories and application level protocols as required (see
40   * the constructor and the SSL transport for examples).
41   */
42  public class TcpConnector extends AbstractConnector
43  {
44      public static final String TCP = "tcp";
45  
46      /** Property can be set on the endpoint to configure how the socket is managed */
47      public static final String KEEP_SEND_SOCKET_OPEN_PROPERTY = "keepSendSocketOpen";
48      public static final int DEFAULT_SOCKET_TIMEOUT = INT_VALUE_NOT_SET;
49      public static final int DEFAULT_SO_LINGER = INT_VALUE_NOT_SET;
50      public static final int DEFAULT_BUFFER_SIZE = INT_VALUE_NOT_SET;
51      public static final int DEFAULT_BACKLOG = INT_VALUE_NOT_SET;
52      public static final int DEFAULT_WAIT_TIMEOUT = INT_VALUE_NOT_SET;
53  
54      // to clarify arg to configureSocket
55      public static final boolean SERVER = false;
56      public static final boolean CLIENT = true;
57  
58      private int clientSoTimeout = DEFAULT_SOCKET_TIMEOUT;
59      private int serverSoTimeout = DEFAULT_SOCKET_TIMEOUT;
60      private int socketMaxWait = DEFAULT_WAIT_TIMEOUT;
61      private int sendBufferSize = DEFAULT_BUFFER_SIZE;
62      private int receiveBufferSize = DEFAULT_BUFFER_SIZE;
63      private int receiveBacklog = DEFAULT_BACKLOG;
64      private boolean sendTcpNoDelay;
65      private Boolean reuseAddress = Boolean.TRUE; // this could be null for Java default
66      private int socketSoLinger = DEFAULT_SO_LINGER;
67      private TcpProtocol tcpProtocol;
68      private AbstractTcpSocketFactory socketFactory;
69      private SimpleServerSocketFactory serverSocketFactory;
70      private GenericKeyedObjectPool socketsPool = new GenericKeyedObjectPool();
71      private int keepAliveTimeout = 0;
72      private ExpiryMonitor keepAliveMonitor;
73  
74      /** 
75       * If set, the socket is not closed after sending a message.  This attribute 
76       * only applies when sending data over a socket (Client).
77       */
78      private boolean keepSendSocketOpen = false;
79  
80      /**
81       * Enables SO_KEEPALIVE behavior on open sockets. This automatically checks 
82       * socket connections that are open but unused for long periods and closes 
83       * them if the connection becomes unavailable.  This is a property on the 
84       * socket itself and is used by a server socket to control whether 
85       * connections to the server are kept alive before they are recycled.
86       */
87      private boolean keepAlive = false;
88  
89      //TODO MULE-2300 remove once fixed
90      private TcpSocketKey lastSocketKey;
91  
92      public TcpConnector(MuleContext context)
93      {
94          super(context);
95          setSocketFactory(new TcpSocketFactory());
96          setServerSocketFactory(new TcpServerSocketFactory());
97          setTcpProtocol(new SafeProtocol());
98      }
99  
100     public void configureSocket(boolean client, Socket socket) throws SocketException
101     {
102         // There is some overhead in setting socket timeout and buffer size, so we're
103         // careful here only to set if needed
104 
105         if (newValue(getReceiveBufferSize(), socket.getReceiveBufferSize()))
106         {
107             socket.setReceiveBufferSize(getReceiveBufferSize());
108         }
109         if (newValue(getSendBufferSize(), socket.getSendBufferSize()))
110         {
111             socket.setSendBufferSize(getSendBufferSize());
112         }
113         if (client)
114         {
115             if (newValue(getClientSoTimeout(), socket.getSoTimeout()))
116             {
117                 socket.setSoTimeout(getClientSoTimeout());
118             }
119         }
120         else
121         {
122             if (newValue(getServerSoTimeout(), socket.getSoTimeout()))
123             {
124                 socket.setSoTimeout(getServerSoTimeout());
125             }
126         }
127         if (newValue(getSocketSoLinger(), socket.getSoLinger()))
128         {
129             socket.setSoLinger(true, getSocketSoLinger());
130         }
131         try
132         {
133             socket.setTcpNoDelay(isSendTcpNoDelay());
134         }
135         catch (SocketException e)
136         {
137             // MULE-2800 - Bug in Solaris
138         }
139         socket.setKeepAlive(isKeepAlive());
140     }
141 
142     private boolean newValue(int parameter, int socketValue)
143     {
144         return parameter != Connector.INT_VALUE_NOT_SET && parameter != socketValue;
145     }
146 
147     @Override
148     protected void doInitialise() throws InitialisationException
149     {
150         socketsPool.setFactory(getSocketFactory());
151         socketsPool.setTestOnBorrow(true);
152         socketsPool.setTestOnReturn(true);
153         socketsPool.setMaxActive(getDispatcherThreadingProfile().getMaxThreadsActive());
154         socketsPool.setMaxIdle(getDispatcherThreadingProfile().getMaxThreadsIdle());
155         socketsPool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK);
156         socketsPool.setMaxWait(socketMaxWait);
157 
158         // Use connector's classloader so that other temporary classloaders
159         // aren't used when things are started lazily or from elsewhere.
160         final String monitorName = String.format("%s%s.socket",
161                                                  ThreadNameHelper.getPrefix(muleContext),
162                                                  getName());
163         keepAliveMonitor = new ExpiryMonitor(monitorName, 1000, this.getClass().getClassLoader());
164     }
165 
166     @Override
167     protected void doDispose()
168     {
169         logger.debug("Closing TCP connector");
170         try
171         {
172             socketsPool.close();
173         }
174         catch (Exception e)
175         {
176             logger.warn("Failed to close dispatcher socket pool: " + e.getMessage());
177         }
178         
179         keepAliveMonitor.dispose();
180     }
181 
182     /**
183      * Lookup a socket in the list of dispatcher sockets but don't create a new
184      * socket
185      */
186     protected Socket getSocket(ImmutableEndpoint endpoint) throws Exception
187     {
188         TcpSocketKey socketKey = new TcpSocketKey(endpoint);
189         if (logger.isDebugEnabled())
190         {
191             logger.debug("borrowing socket for " + socketKey + "/" + socketKey.hashCode());
192             if (null != lastSocketKey)
193             {
194                 logger.debug("same as " + lastSocketKey.hashCode() + "? " + lastSocketKey.equals(socketKey));
195             }
196         }
197         Socket socket = (Socket) socketsPool.borrowObject(socketKey);
198         if (logger.isDebugEnabled())
199         {
200             logger.debug("borrowed socket, "
201                     + (socket.isClosed() ? "closed" : "open") 
202                     + "; debt " + socketsPool.getNumActive());
203         }
204         return socket;
205     }
206 
207     void releaseSocket(Socket socket, ImmutableEndpoint endpoint) throws Exception
208     {
209         TcpSocketKey socketKey = new TcpSocketKey(endpoint);
210         lastSocketKey = socketKey;
211         socketsPool.returnObject(socketKey, socket);
212         if (logger.isDebugEnabled())
213         {
214             logger.debug("returning socket for " + socketKey.hashCode());
215             logger.debug("returned socket; debt " + socketsPool.getNumActive());
216         }
217     }
218 
219     public OutputStream getOutputStream(final ImmutableEndpoint endpoint, MuleMessage message)
220             throws MuleException
221     {
222         final Socket socket;
223         try
224         {
225             socket = getSocket(endpoint);
226         }
227         catch (Exception e)
228         {
229             throw new MessagingException(CoreMessages.failedToGetOutputStream(), message, e);
230         }
231         if (socket == null)
232         {
233             // This shouldn't happen
234             throw new IllegalStateException("could not get socket for endpoint: "
235                     + endpoint.getEndpointURI().getAddress());
236         }
237         try
238         {
239             return new CallbackOutputStream(
240                     new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())),
241                     new CallbackOutputStream.Callback()
242                     {
243                         public void onClose() throws Exception
244                         {
245                             releaseSocket(socket, endpoint);
246                         }
247                     });
248         }
249         catch (IOException e)
250         {
251             throw new MessagingException(CoreMessages.failedToGetOutputStream(), message, e);
252         }
253     }
254 
255     @Override
256     protected void doConnect() throws Exception
257     {
258         // template method
259     }
260 
261     @Override
262     protected void doDisconnect() throws Exception
263     {
264         socketsPool.clear();
265     }
266 
267     @Override
268     protected void doStart() throws MuleException
269     {
270         // template method
271     }
272 
273     @Override
274     protected void doStop() throws MuleException
275     {
276         // template method
277     }
278 
279     public String getProtocol()
280     {
281         return TCP;
282     }
283 
284     // getters and setters ---------------------------------------------------------
285 
286     public boolean isKeepSendSocketOpen()
287     {
288         return keepSendSocketOpen;
289     }
290 
291     public void setKeepSendSocketOpen(boolean keepSendSocketOpen)
292     {
293         this.keepSendSocketOpen = keepSendSocketOpen;
294     }
295 
296     /**
297      * A shorthand property setting timeout for both SEND and RECEIVE sockets.
298      *
299      * @deprecated The time out should be set explicitly for each
300      */
301     @Deprecated
302     public void setTimeout(int timeout)
303     {
304         setClientSoTimeout(timeout);
305         setServerSoTimeout(timeout);
306     }
307 
308     public int getClientSoTimeout()
309     {
310         return this.clientSoTimeout;
311     }
312 
313     public void setClientSoTimeout(int timeout)
314     {
315         this.clientSoTimeout = valueOrDefault(timeout, 0, DEFAULT_SOCKET_TIMEOUT);
316     }
317 
318     public int getServerSoTimeout()
319     {
320         return serverSoTimeout;
321     }
322 
323     public void setServerSoTimeout(int timeout)
324     {
325         this.serverSoTimeout = valueOrDefault(timeout, 0, DEFAULT_SOCKET_TIMEOUT);
326     }
327 
328     public int getSocketMaxWait()
329     {
330         return socketMaxWait;
331     }
332 
333     public void setSocketMaxWait(int timeout)
334     {
335         this.socketMaxWait = valueOrDefault(timeout, 0, DEFAULT_WAIT_TIMEOUT);
336     }
337 
338     /** @deprecated Should use {@link #getSendBufferSize()} or {@link #getReceiveBufferSize()} */
339     @Deprecated
340     public int getBufferSize()
341     {
342         return sendBufferSize;
343     }
344 
345     /** @deprecated Should use {@link #setSendBufferSize(int)} or {@link #setReceiveBufferSize(int)} */
346     @Deprecated
347     public void setBufferSize(int bufferSize)
348     {
349         sendBufferSize = valueOrDefault(bufferSize, 1, DEFAULT_BUFFER_SIZE);
350     }
351 
352     public int getSendBufferSize()
353     {
354         return sendBufferSize;
355     }
356 
357     public void setSendBufferSize(int bufferSize)
358     {
359         sendBufferSize = valueOrDefault(bufferSize, 1, DEFAULT_BUFFER_SIZE);
360     }
361 
362     public int getReceiveBufferSize()
363     {
364         return receiveBufferSize;
365     }
366 
367     public void setReceiveBufferSize(int bufferSize)
368     {
369         receiveBufferSize = valueOrDefault(bufferSize, 1, DEFAULT_BUFFER_SIZE);
370     }
371 
372     public int getReceiveBacklog()
373     {
374         return receiveBacklog;
375     }
376 
377     public void setReceiveBacklog(int receiveBacklog)
378     {
379         this.receiveBacklog = valueOrDefault(receiveBacklog, 0, DEFAULT_BACKLOG);
380     }
381 
382     public int getSocketSoLinger()
383     {
384         return socketSoLinger;
385     }
386 
387     public void setSocketSoLinger(int soLinger)
388     {
389         this.socketSoLinger = valueOrDefault(soLinger, 0, INT_VALUE_NOT_SET);
390     }
391 
392     /**
393      * @deprecated should use {@link #getReceiveBacklog()}
394      */
395     @Deprecated
396     public int getBacklog()
397     {
398         return receiveBacklog;
399     }
400 
401     /**
402      * @param backlog
403      * @deprecated should use {@link #setReceiveBacklog(int)}
404      */
405     @Deprecated
406     public void setBacklog(int backlog)
407     {
408         this.receiveBacklog = backlog;
409     }
410 
411     public TcpProtocol getTcpProtocol()
412     {
413         return tcpProtocol;
414     }
415 
416     public void setTcpProtocol(TcpProtocol tcpProtocol)
417     {
418         this.tcpProtocol = tcpProtocol;
419     }
420 
421     @Override
422     public boolean isResponseEnabled()
423     {
424         return true;
425     }
426 
427     public boolean isKeepAlive()
428     {
429         return keepAlive;
430     }
431 
432     public void setKeepAlive(boolean keepAlive)
433     {
434         this.keepAlive = keepAlive;
435     }
436 
437     public boolean isSendTcpNoDelay()
438     {
439         return sendTcpNoDelay;
440     }
441 
442     public void setSendTcpNoDelay(boolean sendTcpNoDelay)
443     {
444         this.sendTcpNoDelay = sendTcpNoDelay;
445     }
446 
447     protected void setSocketFactory(AbstractTcpSocketFactory socketFactory)
448     {
449         this.socketFactory = socketFactory;
450     }
451 
452     protected AbstractTcpSocketFactory getSocketFactory()
453     {
454         return socketFactory;
455     }
456 
457     public SimpleServerSocketFactory getServerSocketFactory()
458     {
459         return serverSocketFactory;
460     }
461 
462     public void setServerSocketFactory(SimpleServerSocketFactory serverSocketFactory)
463     {
464         this.serverSocketFactory = serverSocketFactory;
465     }
466 
467     protected ServerSocket getServerSocket(URI uri) throws IOException
468     {
469         return getServerSocketFactory().createServerSocket(uri, getReceiveBacklog(), isReuseAddress());
470     }
471 
472     private static int valueOrDefault(int value, int threshhold, int deflt)
473     {
474         if (value < threshhold)
475         {
476             return deflt;
477         }
478         else
479         {
480             return value;
481         }
482     }
483 
484     /**
485      * @return true if the server socket sets SO_REUSEADDRESS before opening
486      */
487     public Boolean isReuseAddress()
488     {
489         return reuseAddress;
490     }
491 
492     /**
493      * This allows closed sockets to be reused while they are still in TIME_WAIT state
494      *
495      * @param reuseAddress Whether the server socket sets SO_REUSEADDRESS before opening
496      */
497     public void setReuseAddress(Boolean reuseAddress)
498     {
499         this.reuseAddress = reuseAddress;
500     }
501 
502     public ExpiryMonitor getKeepAliveMonitor()
503     {
504         return keepAliveMonitor;
505     }
506     
507     /**
508      * @return keep alive timeout in Milliseconds
509      */
510     public int getKeepAliveTimeout()
511     {
512         return keepAliveTimeout;
513     }
514     
515     /**
516      * Sets the keep alive timeout (in Milliseconds)
517      */
518     public void setKeepAliveTimeout(int keepAliveTimeout)
519     {
520         this.keepAliveTimeout = keepAliveTimeout;
521     }
522     
523     @Override
524     public void setDispatcherFactory(MessageDispatcherFactory dispatcherFactory)
525     {
526         if (this.dispatcherFactory == null) {
527             super.setDispatcherFactory(dispatcherFactory);
528         }
529     }
530 
531     public ConfigurableKeyedObjectPool getDispatchers()
532     {
533         return dispatchers;
534     }
535 
536     public int getSocketsPoolMaxActive()
537     {
538         return socketsPool.getMaxActive();
539     }
540 
541     public int getSocketsPoolMaxIdle()
542     {
543         return socketsPool.getMaxIdle();
544     }
545 
546     public int getSocketsPoolNumActive()
547     {
548         return socketsPool.getNumActive();
549     }
550 
551     public long getSocketsPoolMaxWait()
552     {
553         return socketsPool.getMaxWait();
554     }
555 
556 }