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