View Javadoc

1   /*
2    * $Id: TcpConnector.java 7976 2007-08-21 14:26:13Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.providers.tcp;
12  
13  import org.mule.config.i18n.CoreMessages;
14  import org.mule.impl.model.streaming.CallbackOutputStream;
15  import org.mule.providers.AbstractConnector;
16  import org.mule.providers.tcp.i18n.TcpMessages;
17  import org.mule.providers.tcp.protocols.DefaultProtocol;
18  import org.mule.umo.MessagingException;
19  import org.mule.umo.UMOException;
20  import org.mule.umo.UMOMessage;
21  import org.mule.umo.endpoint.UMOImmutableEndpoint;
22  import org.mule.umo.lifecycle.InitialisationException;
23  import org.mule.util.ClassUtils;
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.URI;
32  
33  import org.apache.commons.pool.impl.GenericKeyedObjectPool;
34  
35  /**
36   * <code>TcpConnector</code> can bind or sent to a given TCP port on a given host.
37   * Other socket-based transports can be built on top of this class by providing the
38   * appropriate socket factories and application level protocols as required (see
39   * the constructor and the SSL transport for examples).
40   */
41  public class TcpConnector extends AbstractConnector
42  {
43      /**
44       * Property can be set on the endpoint to configure how the socket is managed
45       */
46      public static final String KEEP_SEND_SOCKET_OPEN_PROPERTY = "keepSendSocketOpen";
47      public static final int DEFAULT_SOCKET_TIMEOUT = INT_VALUE_NOT_SET;
48      public static final int DEFAULT_BUFFER_SIZE = INT_VALUE_NOT_SET;
49      public static final int DEFAULT_BACKLOG = INT_VALUE_NOT_SET;
50  
51      private int sendTimeout = DEFAULT_SOCKET_TIMEOUT;
52      private int receiveTimeout = DEFAULT_SOCKET_TIMEOUT;
53      private int sendBufferSize = DEFAULT_BUFFER_SIZE;
54      private int receiveBufferSize = DEFAULT_BUFFER_SIZE;
55      private int receiveBacklog = DEFAULT_BACKLOG;
56      private boolean sendTcpNoDelay;
57      private boolean validateConnections = true;
58      private Boolean reuseAddress = null; // not set - Java default
59      private int socketLinger = INT_VALUE_NOT_SET;
60      private String tcpProtocolClassName;
61      private TcpProtocol tcpProtocol;
62      private boolean keepSendSocketOpen = false;
63      private boolean keepAlive = false;
64      private PooledSocketFactory socketFactory;
65      private SimpleServerSocketFactory serverSocketFactory;
66      private GenericKeyedObjectPool dispatcherSocketsPool = new GenericKeyedObjectPool();
67  
68      public TcpConnector()
69      {
70          setSocketFactory (new TcpSocketFactory());
71          setServerSocketFactory(new TcpServerSocketFactory());
72          setTcpProtocolClassName(DefaultProtocol.class.getName());
73      }
74  
75      protected void doInitialise() throws InitialisationException
76      {
77          if (tcpProtocol == null)
78          {
79              try
80              {
81                  tcpProtocol = (TcpProtocol) ClassUtils.instanciateClass(tcpProtocolClassName, null);
82              }
83              catch (Exception e)
84              {
85                  throw new InitialisationException(TcpMessages.failedToInitMessageReader(), e);
86              }
87          }
88  
89          dispatcherSocketsPool.setFactory(getSocketFactory());
90          dispatcherSocketsPool.setTestOnBorrow(true);
91          dispatcherSocketsPool.setTestOnReturn(true);
92          //There should only be one pooled instance per socket (key)        
93          dispatcherSocketsPool.setMaxActive(1);
94      }
95  
96      protected void doDispose()
97      {
98          logger.debug("Closing TCP connector");
99          try
100         {
101             dispatcherSocketsPool.close();
102         }
103         catch (Exception e)
104         {
105             logger.warn("Failed to close dispatcher socket pool: " + e.getMessage());
106         }
107     }
108 
109     /**
110      * Lookup a socket in the list of dispatcher sockets but don't create a new
111      * socket
112      */
113     protected Socket getSocket(UMOImmutableEndpoint endpoint) throws Exception
114     {
115         Socket socket = (Socket) dispatcherSocketsPool.borrowObject(endpoint);
116         if (logger.isDebugEnabled())
117         {
118             logger.debug("borrowing socket; debt " + dispatcherSocketsPool.getNumActive());
119         }
120         return socket;
121     }
122 
123     void releaseSocket(Socket socket, UMOImmutableEndpoint endpoint) throws Exception
124     {
125         dispatcherSocketsPool.returnObject(endpoint, socket);
126         if (logger.isDebugEnabled())
127         {
128             logger.debug("returned socket; debt " + dispatcherSocketsPool.getNumActive());
129         }
130     }
131 
132     public OutputStream getOutputStream(final UMOImmutableEndpoint endpoint, UMOMessage message)
133             throws UMOException
134     {
135         final Socket socket;
136         try
137         {
138             socket = getSocket(endpoint);
139         }
140         catch (Exception e)
141         {
142             throw new MessagingException(CoreMessages.failedToGetOutputStream(), message, e);
143         }
144         if (socket == null)
145         {
146             // This shouldn't happen
147             throw new IllegalStateException("could not get socket for endpoint: "
148                                             + endpoint.getEndpointURI().getAddress());
149         }
150         try
151         {
152             return new CallbackOutputStream(
153                     new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())),
154                     new CallbackOutputStream.Callback()
155                     {
156                         public void onClose() throws Exception
157                         {
158                             releaseSocket(socket, endpoint);
159                         }
160                     });
161         }
162         catch (IOException e)
163         {
164             throw new MessagingException(CoreMessages.failedToGetOutputStream(), message, e);
165         }
166     }
167 
168     protected void doConnect() throws Exception
169     {
170         // template method
171     }
172 
173     protected void doDisconnect() throws Exception
174     {
175         dispatcherSocketsPool.clear();
176     }
177 
178     protected void doStart() throws UMOException
179     {
180         // template method
181     }
182 
183     protected void doStop() throws UMOException
184     {
185         // template method
186     }
187 
188     public String getProtocol()
189     {
190         return "tcp";
191     }
192 
193     // getters and setters ---------------------------------------------------------
194     
195     public boolean isKeepSendSocketOpen()
196     {
197         return keepSendSocketOpen;
198     }
199 
200     public void setKeepSendSocketOpen(boolean keepSendSocketOpen)
201     {
202         this.keepSendSocketOpen = keepSendSocketOpen;
203     }
204 
205     /**
206      * A shorthand property setting timeout for both SEND and RECEIVE sockets.
207      * @deprecated The time out should be set explicitly for each
208      */
209     public void setTimeout(int timeout)
210     {
211         setSendTimeout(timeout);
212         setReceiveTimeout(timeout);
213     }
214 
215     public int getSendTimeout()
216     {
217         return this.sendTimeout;
218     }
219 
220     public void setSendTimeout(int timeout)
221     {
222         this.sendTimeout = valueOrDefault(timeout, 0, DEFAULT_SOCKET_TIMEOUT);
223     }
224 
225     public int getReceiveTimeout()
226     {
227         return receiveTimeout;
228     }
229 
230     public void setReceiveTimeout(int timeout)
231     {
232         this.receiveTimeout = valueOrDefault(timeout, 0, DEFAULT_SOCKET_TIMEOUT);
233     }
234 
235     /**
236      * @deprecated Should use {@link #getSendBufferSize()} or {@link #getReceiveBufferSize()}
237      */
238     public int getBufferSize()
239     {
240         return sendBufferSize;
241     }
242 
243     /**
244      * @deprecated Should use {@link #setSendBufferSize(int)} or {@link #setReceiveBufferSize(int)}
245      */
246     public void setBufferSize(int bufferSize)
247     {
248         sendBufferSize = valueOrDefault(bufferSize, 1, DEFAULT_BUFFER_SIZE);
249     }
250 
251     public int getSendBufferSize()
252     {
253         return sendBufferSize;
254     }
255 
256     public void setSendBufferSize(int bufferSize)
257     {
258         sendBufferSize = valueOrDefault(bufferSize, 1, DEFAULT_BUFFER_SIZE);
259     }
260 
261     public int getReceiveBufferSize()
262     {
263         return receiveBufferSize;
264     }
265 
266     public void setReceiveBufferSize(int bufferSize)
267     {
268         receiveBufferSize = valueOrDefault(bufferSize, 1, DEFAULT_BUFFER_SIZE);
269     }
270 
271     public int getReceiveBacklog()
272     {
273         return receiveBacklog;
274     }
275 
276     public void setReceiveBacklog(int receiveBacklog)
277     {
278         this.receiveBacklog = valueOrDefault(receiveBacklog, 0, DEFAULT_BACKLOG);
279     }
280 
281     public int getSendSocketLinger()
282     {
283         return socketLinger;
284     }
285 
286     public void setSendSocketLinger(int soLinger)
287     {
288         this.socketLinger = valueOrDefault(soLinger, 0, INT_VALUE_NOT_SET);
289     }
290 
291     /**
292      *
293      * @return
294      * @deprecated should use {@link #getReceiveBacklog()}
295      */
296     public int getBacklog()
297     {
298         return receiveBacklog;
299     }
300 
301     /**
302      *
303      * @param backlog
304      * @deprecated should use {@link #setReceiveBacklog(int)}
305      */
306     public void setBacklog(int backlog)
307     {
308         this.receiveBacklog = backlog;
309     }
310 
311     public TcpProtocol getTcpProtocol()
312     {
313         return tcpProtocol;
314     }
315 
316     public void setTcpProtocol(TcpProtocol tcpProtocol)
317     {
318         this.tcpProtocol = tcpProtocol;
319     }
320 
321     public String getTcpProtocolClassName()
322     {
323         return tcpProtocolClassName;
324     }
325 
326     public void setTcpProtocolClassName(String protocolClassName)
327     {
328         this.tcpProtocolClassName = protocolClassName;
329     }
330 
331     public boolean isRemoteSyncEnabled()
332     {
333         return true;
334     }
335 
336     public boolean isKeepAlive()
337     {
338         return keepAlive;
339     }
340 
341     public void setKeepAlive(boolean keepAlive)
342     {
343         this.keepAlive = keepAlive;
344     }
345 
346     public boolean isSendTcpNoDelay()
347     {
348         return sendTcpNoDelay;
349     }
350 
351     public void setSendTcpNoDelay(boolean sendTcpNoDelay)
352     {
353         this.sendTcpNoDelay = sendTcpNoDelay;
354     }
355     
356     protected void setSocketFactory(PooledSocketFactory socketFactory)
357     {
358         this.socketFactory = socketFactory;
359     }
360 
361     protected PooledSocketFactory getSocketFactory()
362     {
363         return socketFactory;
364     }
365 
366     public SimpleServerSocketFactory getServerSocketFactory()
367     {
368         return serverSocketFactory;
369     }
370 
371     public void setServerSocketFactory(SimpleServerSocketFactory serverSocketFactory)
372     {
373         this.serverSocketFactory = serverSocketFactory;
374     }
375 
376     protected ServerSocket getServerSocket(URI uri) throws IOException
377     {
378         return getServerSocketFactory().createServerSocket(uri, getReceiveBacklog(), isReuseAddress());
379     }
380     
381     private static int valueOrDefault(int value, int threshhold, int deflt)
382     {
383         if (value < threshhold)
384         {
385             return deflt;
386         }
387         else 
388         {
389             return value;    
390         }
391     }
392 
393     /**
394      * Should the connection be checked before sending data?
395      *
396      * @return If true, the message adapter opens and closes the socket on intialisation.
397      */
398     public boolean isValidateConnections() {
399         return validateConnections;
400     }
401 
402     /**
403      * @see #isValidateConnections()
404      * @param validateConnections If true, the message adapter opens and closes the socket on intialisation.
405      */
406     public void setValidateConnections(boolean validateConnections) {
407         this.validateConnections = validateConnections;
408     }
409 
410     /**
411      * @return true if the server socket sets SO_REUSEADDRESS before opening
412      */
413     public Boolean isReuseAddress()
414     {
415         return reuseAddress;
416     }
417 
418     /**
419      * This allows closed sockets to be reused while they are still in TIME_WAIT state
420      *
421      * @param reuseAddress Whether the server socket sets SO_REUSEADDRESS before opening
422      */
423     public void setReuseAddress(Boolean reuseAddress)
424     {
425         this.reuseAddress = reuseAddress;
426     }
427 
428 }