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