Coverage Report - org.mule.transport.tcp.TcpConnector
 
Classes in this File Line Coverage Branch Coverage Complexity
TcpConnector
78%
108/139
67%
20/30
1.396
TcpConnector$1
0%
0/3
N/A
1.396
 
 1  
 /*
 2  
  * $Id: TcpConnector.java 12117 2008-06-20 09:40:51Z 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.transport.tcp;
 12  
 
 13  
 import org.mule.api.MessagingException;
 14  
 import org.mule.api.MuleException;
 15  
 import org.mule.api.MuleMessage;
 16  
 import org.mule.api.endpoint.ImmutableEndpoint;
 17  
 import org.mule.api.lifecycle.InitialisationException;
 18  
 import org.mule.api.transport.Connector;
 19  
 import org.mule.config.i18n.CoreMessages;
 20  
 import org.mule.model.streaming.CallbackOutputStream;
 21  
 import org.mule.transport.AbstractConnector;
 22  
 import org.mule.transport.tcp.protocols.SafeProtocol;
 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  
 
 53  
     // to clarify arg to configureSocket
 54  
     public static final boolean SERVER = false;
 55  
     public static final boolean CLIENT = true;
 56  
 
 57  132
     private int clientSoTimeout = DEFAULT_SOCKET_TIMEOUT;
 58  132
     private int serverSoTimeout = DEFAULT_SOCKET_TIMEOUT;
 59  132
     private int sendBufferSize = DEFAULT_BUFFER_SIZE;
 60  132
     private int receiveBufferSize = DEFAULT_BUFFER_SIZE;
 61  132
     private int receiveBacklog = DEFAULT_BACKLOG;
 62  
     private boolean sendTcpNoDelay;
 63  132
     private boolean validateConnections = true;
 64  132
     private Boolean reuseAddress = Boolean.TRUE; // this could be null for Java default
 65  132
     private int socketSoLinger = DEFAULT_SO_LINGER;
 66  
     private TcpProtocol tcpProtocol;
 67  132
     private boolean keepSendSocketOpen = false;
 68  132
     private boolean keepAlive = false;
 69  
     private AbstractTcpSocketFactory socketFactory;
 70  
     private SimpleServerSocketFactory serverSocketFactory;
 71  132
     private GenericKeyedObjectPool socketsPool = new GenericKeyedObjectPool();
 72  132
     private int keepAliveTimeout = 0;
 73  
     private ExpiryMonitor keepAliveMonitor;
 74  
 
 75  
     //TODO MULE-2300 remove once fixed
 76  
     private TcpSocketKey lastSocketKey;
 77  
 
 78  
     public TcpConnector()
 79  132
     {
 80  132
         setSocketFactory(new TcpSocketFactory());
 81  132
         setServerSocketFactory(new TcpServerSocketFactory());
 82  132
         setTcpProtocol(new SafeProtocol());
 83  132
         keepAliveMonitor = new ExpiryMonitor("SocketTimeoutMonitor", 1000);
 84  132
     }
 85  
 
 86  
     public void configureSocket(boolean client, Socket socket) throws SocketException
 87  
     {
 88  
         // There is some overhead in setting socket timeout and buffer size, so we're
 89  
         // careful here only to set if needed
 90  
 
 91  2953
         if (newValue(getReceiveBufferSize(), socket.getReceiveBufferSize()))
 92  
         {
 93  70
             socket.setReceiveBufferSize(getReceiveBufferSize());
 94  
         }
 95  2953
         if (newValue(getSendBufferSize(), socket.getSendBufferSize()))
 96  
         {
 97  70
             socket.setSendBufferSize(getSendBufferSize());
 98  
         }
 99  2953
         if (client)
 100  
         {
 101  1491
             if (newValue(getClientSoTimeout(), socket.getSoTimeout()))
 102  
             {
 103  54
                 socket.setSoTimeout(getClientSoTimeout());
 104  
             }
 105  
         }
 106  
         else
 107  
         {
 108  1462
             if (newValue(getServerSoTimeout(), socket.getSoTimeout()))
 109  
             {
 110  31
                 socket.setSoTimeout(getServerSoTimeout());
 111  
             }
 112  
         }
 113  2953
         if (newValue(getSocketSoLinger(), socket.getSoLinger()))
 114  
         {
 115  12
             socket.setSoLinger(true, getSocketSoLinger());
 116  
         }
 117  
         try
 118  
         {
 119  2953
             socket.setTcpNoDelay(isSendTcpNoDelay());
 120  
         }
 121  0
         catch (SocketException e)
 122  
         {
 123  
             // MULE-2800 - Bug in Solaris
 124  2953
         }
 125  2953
         socket.setKeepAlive(isKeepAlive());
 126  2953
     }
 127  
 
 128  
     private boolean newValue(int parameter, int socketValue)
 129  
     {
 130  11812
         return parameter != Connector.INT_VALUE_NOT_SET && parameter != socketValue;
 131  
     }
 132  
 
 133  
     protected void doInitialise() throws InitialisationException
 134  
     {
 135  132
         socketsPool.setFactory(getSocketFactory());
 136  132
         socketsPool.setTestOnBorrow(true);
 137  132
         socketsPool.setTestOnReturn(true);
 138  
         //There should only be one pooled instance per socket (key)
 139  132
         socketsPool.setMaxActive(1);
 140  132
         socketsPool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK);
 141  132
     }
 142  
 
 143  
     protected void doDispose()
 144  
     {
 145  152
         logger.debug("Closing TCP connector");
 146  
         try
 147  
         {
 148  152
             socketsPool.close();
 149  
         }
 150  0
         catch (Exception e)
 151  
         {
 152  0
             logger.warn("Failed to close dispatcher socket pool: " + e.getMessage());
 153  152
         }
 154  
         
 155  152
         keepAliveMonitor.dispose();
 156  152
     }
 157  
 
 158  
     /**
 159  
      * Lookup a socket in the list of dispatcher sockets but don't create a new
 160  
      * socket
 161  
      */
 162  
     protected Socket getSocket(ImmutableEndpoint endpoint) throws Exception
 163  
     {
 164  2923
         TcpSocketKey socketKey = new TcpSocketKey(endpoint);
 165  2923
         if (logger.isDebugEnabled())
 166  
         {
 167  0
             logger.debug("borrowing socket for " + socketKey + "/" + socketKey.hashCode());
 168  0
             if (null != lastSocketKey)
 169  
             {
 170  0
                 logger.debug("same as " + lastSocketKey.hashCode() + "? " + lastSocketKey.equals(socketKey));
 171  
             }
 172  
         }
 173  2923
         Socket socket = (Socket) socketsPool.borrowObject(socketKey);
 174  1837
         if (logger.isDebugEnabled())
 175  
         {
 176  0
             logger.debug("borrowed socket, "
 177  
                     + (socket.isClosed() ? "closed" : "open") 
 178  
                     + "; debt " + socketsPool.getNumActive());
 179  
         }
 180  1837
         return socket;
 181  
     }
 182  
 
 183  
     void releaseSocket(Socket socket, ImmutableEndpoint endpoint) throws Exception
 184  
     {
 185  1837
         TcpSocketKey socketKey = new TcpSocketKey(endpoint);
 186  1837
         lastSocketKey = socketKey;
 187  1837
         socketsPool.returnObject(socketKey, socket);
 188  1837
         if (logger.isDebugEnabled())
 189  
         {
 190  0
             logger.debug("returning socket for " + socketKey.hashCode());
 191  0
             logger.debug("returned socket; debt " + socketsPool.getNumActive());
 192  
         }
 193  1837
     }
 194  
 
 195  
     public OutputStream getOutputStream(final ImmutableEndpoint endpoint, MuleMessage message)
 196  
             throws MuleException
 197  
     {
 198  
         final Socket socket;
 199  
         try
 200  
         {
 201  0
             socket = getSocket(endpoint);
 202  
         }
 203  0
         catch (Exception e)
 204  
         {
 205  0
             throw new MessagingException(CoreMessages.failedToGetOutputStream(), message, e);
 206  0
         }
 207  0
         if (socket == null)
 208  
         {
 209  
             // This shouldn't happen
 210  0
             throw new IllegalStateException("could not get socket for endpoint: "
 211  
                     + endpoint.getEndpointURI().getAddress());
 212  
         }
 213  
         try
 214  
         {
 215  0
             return new CallbackOutputStream(
 216  
                     new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())),
 217  
                     new CallbackOutputStream.Callback()
 218  
                     {
 219  0
                         public void onClose() throws Exception
 220  
                         {
 221  0
                             releaseSocket(socket, endpoint);
 222  0
                         }
 223  
                     });
 224  
         }
 225  0
         catch (IOException e)
 226  
         {
 227  0
             throw new MessagingException(CoreMessages.failedToGetOutputStream(), message, e);
 228  
         }
 229  
     }
 230  
 
 231  
     protected void doConnect() throws Exception
 232  
     {
 233  
         // template method
 234  128
     }
 235  
 
 236  
     protected void doDisconnect() throws Exception
 237  
     {
 238  128
         socketsPool.clear();
 239  128
     }
 240  
 
 241  
     protected void doStart() throws MuleException
 242  
     {
 243  
         // template method
 244  128
     }
 245  
 
 246  
     protected void doStop() throws MuleException
 247  
     {
 248  
         // template method
 249  128
     }
 250  
 
 251  
     public String getProtocol()
 252  
     {
 253  3139
         return TCP;
 254  
     }
 255  
 
 256  
     // getters and setters ---------------------------------------------------------
 257  
 
 258  
     public boolean isKeepSendSocketOpen()
 259  
     {
 260  1823
         return keepSendSocketOpen;
 261  
     }
 262  
 
 263  
     public void setKeepSendSocketOpen(boolean keepSendSocketOpen)
 264  
     {
 265  34
         this.keepSendSocketOpen = keepSendSocketOpen;
 266  34
     }
 267  
 
 268  
     /**
 269  
      * A shorthand property setting timeout for both SEND and RECEIVE sockets.
 270  
      *
 271  
      * @deprecated The time out should be set explicitly for each
 272  
      */
 273  
     public void setTimeout(int timeout)
 274  
     {
 275  0
         setClientSoTimeout(timeout);
 276  0
         setServerSoTimeout(timeout);
 277  0
     }
 278  
 
 279  
     public int getClientSoTimeout()
 280  
     {
 281  1553
         return this.clientSoTimeout;
 282  
     }
 283  
 
 284  
     public void setClientSoTimeout(int timeout)
 285  
     {
 286  20
         this.clientSoTimeout = valueOrDefault(timeout, 0, DEFAULT_SOCKET_TIMEOUT);
 287  20
     }
 288  
 
 289  
     public int getServerSoTimeout()
 290  
     {
 291  1501
         return serverSoTimeout;
 292  
     }
 293  
 
 294  
     public void setServerSoTimeout(int timeout)
 295  
     {
 296  16
         this.serverSoTimeout = valueOrDefault(timeout, 0, DEFAULT_SOCKET_TIMEOUT);
 297  16
     }
 298  
 
 299  
     /** @deprecated Should use {@link #getSendBufferSize()} or {@link #getReceiveBufferSize()} */
 300  
     public int getBufferSize()
 301  
     {
 302  0
         return sendBufferSize;
 303  
     }
 304  
 
 305  
     /** @deprecated Should use {@link #setSendBufferSize(int)} or {@link #setReceiveBufferSize(int)} */
 306  
     public void setBufferSize(int bufferSize)
 307  
     {
 308  0
         sendBufferSize = valueOrDefault(bufferSize, 1, DEFAULT_BUFFER_SIZE);
 309  0
     }
 310  
 
 311  
     public int getSendBufferSize()
 312  
     {
 313  3029
         return sendBufferSize;
 314  
     }
 315  
 
 316  
     public void setSendBufferSize(int bufferSize)
 317  
     {
 318  12
         sendBufferSize = valueOrDefault(bufferSize, 1, DEFAULT_BUFFER_SIZE);
 319  12
     }
 320  
 
 321  
     public int getReceiveBufferSize()
 322  
     {
 323  3025
         return receiveBufferSize;
 324  
     }
 325  
 
 326  
     public void setReceiveBufferSize(int bufferSize)
 327  
     {
 328  8
         receiveBufferSize = valueOrDefault(bufferSize, 1, DEFAULT_BUFFER_SIZE);
 329  8
     }
 330  
 
 331  
     public int getReceiveBacklog()
 332  
     {
 333  116
         return receiveBacklog;
 334  
     }
 335  
 
 336  
     public void setReceiveBacklog(int receiveBacklog)
 337  
     {
 338  4
         this.receiveBacklog = valueOrDefault(receiveBacklog, 0, DEFAULT_BACKLOG);
 339  4
     }
 340  
 
 341  
     public int getSocketSoLinger()
 342  
     {
 343  2965
         return socketSoLinger;
 344  
     }
 345  
 
 346  
     public void setSocketSoLinger(int soLinger)
 347  
     {
 348  6
         this.socketSoLinger = valueOrDefault(soLinger, 0, INT_VALUE_NOT_SET);
 349  6
     }
 350  
 
 351  
     /**
 352  
      * @return
 353  
      * @deprecated should use {@link #getReceiveBacklog()}
 354  
      */
 355  
     public int getBacklog()
 356  
     {
 357  0
         return receiveBacklog;
 358  
     }
 359  
 
 360  
     /**
 361  
      * @param backlog
 362  
      * @deprecated should use {@link #setReceiveBacklog(int)}
 363  
      */
 364  
     public void setBacklog(int backlog)
 365  
     {
 366  0
         this.receiveBacklog = backlog;
 367  0
     }
 368  
 
 369  
     public TcpProtocol getTcpProtocol()
 370  
     {
 371  4733
         return tcpProtocol;
 372  
     }
 373  
 
 374  
     public void setTcpProtocol(TcpProtocol tcpProtocol)
 375  
     {
 376  214
         this.tcpProtocol = tcpProtocol;
 377  214
     }
 378  
 
 379  
     public boolean isRemoteSyncEnabled()
 380  
     {
 381  198
         return true;
 382  
     }
 383  
 
 384  
     public boolean isKeepAlive()
 385  
     {
 386  2955
         return keepAlive;
 387  
     }
 388  
 
 389  
     public void setKeepAlive(boolean keepAlive)
 390  
     {
 391  4
         this.keepAlive = keepAlive;
 392  4
     }
 393  
 
 394  
     public boolean isSendTcpNoDelay()
 395  
     {
 396  2953
         return sendTcpNoDelay;
 397  
     }
 398  
 
 399  
     public void setSendTcpNoDelay(boolean sendTcpNoDelay)
 400  
     {
 401  0
         this.sendTcpNoDelay = sendTcpNoDelay;
 402  0
     }
 403  
 
 404  
     protected void setSocketFactory(AbstractTcpSocketFactory socketFactory)
 405  
     {
 406  132
         this.socketFactory = socketFactory;
 407  132
     }
 408  
 
 409  
     protected AbstractTcpSocketFactory getSocketFactory()
 410  
     {
 411  132
         return socketFactory;
 412  
     }
 413  
 
 414  
     public SimpleServerSocketFactory getServerSocketFactory()
 415  
     {
 416  114
         return serverSocketFactory;
 417  
     }
 418  
 
 419  
     public void setServerSocketFactory(SimpleServerSocketFactory serverSocketFactory)
 420  
     {
 421  132
         this.serverSocketFactory = serverSocketFactory;
 422  132
     }
 423  
 
 424  
     protected ServerSocket getServerSocket(URI uri) throws IOException
 425  
     {
 426  114
         return getServerSocketFactory().createServerSocket(uri, getReceiveBacklog(), isReuseAddress());
 427  
     }
 428  
 
 429  
     private static int valueOrDefault(int value, int threshhold, int deflt)
 430  
     {
 431  66
         if (value < threshhold)
 432  
         {
 433  6
             return deflt;
 434  
         }
 435  
         else
 436  
         {
 437  60
             return value;
 438  
         }
 439  
     }
 440  
 
 441  
     /**
 442  
      * Should the connection be checked before sending data?
 443  
      *
 444  
      * @return If true, the message adapter opens and closes the socket on intialisation.
 445  
      */
 446  
     public boolean isValidateConnections()
 447  
     {
 448  93
         return validateConnections;
 449  
     }
 450  
 
 451  
     /**
 452  
      * @param validateConnections If true, the message adapter opens and closes the socket on intialisation.
 453  
      * @see #isValidateConnections()
 454  
      */
 455  
     public void setValidateConnections(boolean validateConnections)
 456  
     {
 457  20
         this.validateConnections = validateConnections;
 458  20
     }
 459  
 
 460  
     /**
 461  
      * @return true if the server socket sets SO_REUSEADDRESS before opening
 462  
      */
 463  
     public Boolean isReuseAddress()
 464  
     {
 465  118
         return reuseAddress;
 466  
     }
 467  
 
 468  
     /**
 469  
      * This allows closed sockets to be reused while they are still in TIME_WAIT state
 470  
      *
 471  
      * @param reuseAddress Whether the server socket sets SO_REUSEADDRESS before opening
 472  
      */
 473  
     public void setReuseAddress(Boolean reuseAddress)
 474  
     {
 475  10
         this.reuseAddress = reuseAddress;
 476  10
     }
 477  
 
 478  
     public ExpiryMonitor getKeepAliveMonitor()
 479  
     {
 480  3122
         return keepAliveMonitor;
 481  
     }
 482  
     
 483  
     /**
 484  
      * @return keep alive timeout in Milliseconds
 485  
      */
 486  
     public int getKeepAliveTimeout()
 487  
     {
 488  3129
         return keepAliveTimeout;
 489  
     }
 490  
     
 491  
     /**
 492  
      * Sets the keep alive timeout (in Milliseconds)
 493  
      */
 494  
     public void setKeepAliveTimeout(int keepAliveTimeout)
 495  
     {
 496  0
         this.keepAliveTimeout = keepAliveTimeout;
 497  0
     }
 498  
     
 499  
 }