View Javadoc

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