Coverage Report - org.mule.providers.tcp.TcpMessageReceiver
 
Classes in this File Line Coverage Branch Coverage Complexity
TcpMessageReceiver
64%
42/66
57%
16/28
3.1
TcpMessageReceiver$TcpStreamWorker
91%
10/11
100%
2/2
3.1
TcpMessageReceiver$TcpWorker
72%
43/60
61%
22/36
3.1
 
 1  
 /*
 2  
  * $Id: TcpMessageReceiver.java 11728 2008-05-13 07:31:11Z 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.MuleProperties;
 14  
 import org.mule.config.i18n.CoreMessages;
 15  
 import org.mule.impl.MuleMessage;
 16  
 import org.mule.impl.ResponseOutputStream;
 17  
 import org.mule.impl.model.streaming.CloseCountDownInputStream;
 18  
 import org.mule.impl.model.streaming.CloseCountDownOutputStream;
 19  
 import org.mule.providers.AbstractMessageReceiver;
 20  
 import org.mule.providers.AbstractReceiverResourceWorker;
 21  
 import org.mule.providers.ConnectException;
 22  
 import org.mule.providers.tcp.i18n.TcpMessages;
 23  
 import org.mule.umo.TransactionException;
 24  
 import org.mule.umo.UMOComponent;
 25  
 import org.mule.umo.UMOException;
 26  
 import org.mule.umo.UMOTransaction;
 27  
 import org.mule.umo.endpoint.UMOEndpoint;
 28  
 import org.mule.umo.lifecycle.Disposable;
 29  
 import org.mule.umo.lifecycle.DisposeException;
 30  
 import org.mule.umo.lifecycle.InitialisationException;
 31  
 import org.mule.umo.provider.UMOConnector;
 32  
 import org.mule.umo.provider.UMOMessageAdapter;
 33  
 
 34  
 import java.io.BufferedInputStream;
 35  
 import java.io.BufferedOutputStream;
 36  
 import java.io.DataInputStream;
 37  
 import java.io.DataOutputStream;
 38  
 import java.io.IOException;
 39  
 import java.io.InputStream;
 40  
 import java.io.OutputStream;
 41  
 import java.net.ServerSocket;
 42  
 import java.net.Socket;
 43  
 import java.net.SocketAddress;
 44  
 import java.net.SocketTimeoutException;
 45  
 import java.net.URI;
 46  
 import java.net.SocketException;
 47  
 import java.util.Iterator;
 48  
 import java.util.List;
 49  
 
 50  
 import javax.resource.spi.work.Work;
 51  
 import javax.resource.spi.work.WorkException;
 52  
 import javax.resource.spi.work.WorkManager;
 53  
 
 54  
 import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch;
 55  
 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
 56  
 
 57  
 /**
 58  
  * <code>TcpMessageReceiver</code> acts like a TCP server to receive socket
 59  
  * requests.
 60  
  */
 61  204333
 public class TcpMessageReceiver extends AbstractMessageReceiver implements Work
 62  
 {
 63  72
     private ServerSocket serverSocket = null;
 64  
 
 65  
     public TcpMessageReceiver(UMOConnector connector, UMOComponent component, UMOEndpoint endpoint)
 66  
             throws InitialisationException
 67  
     {
 68  72
         super(connector, component, endpoint);
 69  72
     }
 70  
 
 71  
     protected void doConnect() throws ConnectException
 72  
     {
 73  66
         disposing.set(false);
 74  
 
 75  66
         URI uri = endpoint.getEndpointURI().getUri();
 76  
 
 77  
         try
 78  
         {
 79  66
             serverSocket = ((TcpConnector) connector).getServerSocket(uri);
 80  
         }
 81  0
         catch (Exception e)
 82  
         {
 83  0
             throw new org.mule.providers.ConnectException(TcpMessages.failedToBindToUri(uri), e, this);
 84  66
         }
 85  
 
 86  
         try
 87  
         {
 88  66
             getWorkManager().scheduleWork(this, WorkManager.INDEFINITE, null, connector);
 89  
         }
 90  0
         catch (WorkException e)
 91  
         {
 92  0
             throw new ConnectException(CoreMessages.failedToScheduleWork(), e, this);
 93  66
         }
 94  66
     }
 95  
 
 96  
     protected void doDisconnect() throws ConnectException
 97  
     {
 98  
         // this will cause the server thread to quit
 99  66
         disposing.set(true);
 100  
 
 101  
         try
 102  
         {
 103  66
             if (serverSocket != null)
 104  
             {
 105  66
                 if (logger.isDebugEnabled())
 106  
                 {
 107  0
                     logger.debug("Closing: " + serverSocket);
 108  
                 }
 109  66
                 serverSocket.close();
 110  
             }
 111  
         }
 112  0
         catch (IOException e)
 113  
         {
 114  0
             logger.warn("Failed to close server socket: " + e.getMessage(), e);
 115  66
         }
 116  66
     }
 117  
 
 118  
     protected void doStart() throws UMOException
 119  
     {
 120  
         // nothing to do
 121  66
     }
 122  
 
 123  
     protected void doStop() throws UMOException
 124  
     {
 125  
         // nothing to do
 126  66
     }
 127  
 
 128  
     /**
 129  
      * Obtain the serverSocket
 130  
      *
 131  
      * @return the server socket for this server
 132  
      */
 133  
     public ServerSocket getServerSocket()
 134  
     {
 135  0
         return serverSocket;
 136  
     }
 137  
 
 138  
     public void run()
 139  
     {
 140  2819
         while (!disposing.get())
 141  
         {
 142  2753
             if (connector.isStarted() && !disposing.get())
 143  
             {
 144  2753
                 Socket socket = null;
 145  
                 try
 146  
                 {
 147  2753
                     socket = serverSocket.accept();
 148  
 
 149  2687
                     if (logger.isTraceEnabled())
 150  
                     {
 151  0
                         logger.trace("Accepted: " + serverSocket);
 152  
                     }
 153  
                 }
 154  0
                 catch (java.io.InterruptedIOException iie)
 155  
                 {
 156  0
                     if (logger.isDebugEnabled())
 157  
                     {
 158  0
                         logger.debug("Interupted IO doing serverSocket.accept: " + iie.getMessage());
 159  
                     }
 160  
                 }
 161  66
                 catch (Exception e)
 162  
                 {
 163  66
                     if (!connector.isDisposed() && !disposing.get())
 164  
                     {
 165  0
                         logger.warn("Accept failed on socket: " + e, e);
 166  0
                         handleException(new ConnectException(e, this));
 167  
                     }
 168  2687
                 }
 169  
 
 170  2753
                 if (socket != null)
 171  
                 {
 172  
                     try
 173  
                     {
 174  2687
                         Work work = createWork(socket);
 175  
                         try
 176  
                         {
 177  2687
                             getWorkManager().scheduleWork(work, WorkManager.INDEFINITE, null, connector);
 178  
                         }
 179  0
                         catch (WorkException e)
 180  
                         {
 181  0
                             logger.error("Tcp Server receiver Work was not processed: " + e.getMessage(), e);
 182  2687
                         }
 183  
                     }
 184  0
                     catch (IOException e)
 185  
                     {
 186  0
                         handleException(e);
 187  2687
                     }
 188  
                 }
 189  2753
             }
 190  
         }
 191  66
     }
 192  
 
 193  
     public void release()
 194  
     {
 195  
         // template method
 196  0
     }
 197  
 
 198  
     protected void doDispose()
 199  
     {
 200  
         try
 201  
         {
 202  72
             if (serverSocket != null && !serverSocket.isClosed())
 203  
             {
 204  0
                 if (logger.isDebugEnabled())
 205  
                 {
 206  0
                     logger.debug("Closing: " + serverSocket);
 207  
                 }
 208  0
                 serverSocket.close();
 209  
             }
 210  72
             serverSocket = null;
 211  
         }
 212  0
         catch (Exception e)
 213  
         {
 214  0
             logger.error(new DisposeException(TcpMessages.failedToCloseSocket(), e));
 215  72
         }
 216  72
         logger.info("Closed Tcp port");
 217  72
     }
 218  
 
 219  
     protected Work createWork(Socket socket) throws IOException
 220  
     {
 221  2687
         if (endpoint.isStreaming())
 222  
         {
 223  20
             return new TcpStreamWorker(socket, this);
 224  
         }
 225  
         else
 226  
         {
 227  2667
             return new TcpWorker(socket, this);
 228  
         }
 229  
     }
 230  
 
 231  
     protected class TcpWorker extends AbstractReceiverResourceWorker implements Disposable
 232  
     {
 233  2687
         protected Socket socket = null;
 234  
         protected InputStream dataIn;
 235  
         protected OutputStream dataOut;
 236  2687
         protected AtomicBoolean closed = new AtomicBoolean(false);
 237  
         protected TcpProtocol protocol;
 238  
 
 239  
         public TcpWorker(Object resource, AbstractMessageReceiver receiver) throws IOException
 240  2687
         {
 241  2687
             super(resource, receiver, new ResponseOutputStream((Socket) resource));
 242  
 
 243  2687
             this.socket = (Socket) resource;
 244  
 
 245  2687
             final TcpConnector tcpConnector = ((TcpConnector) connector);
 246  2687
             this.protocol = tcpConnector.getTcpProtocol();
 247  
 
 248  
             try
 249  
             {
 250  
                 //There is some overhead in stting socket timeout and buffer size, so we're
 251  
                 //careful here only to set if needed
 252  2687
                 if (tcpConnector.getReceiveBufferSize() != UMOConnector.INT_VALUE_NOT_SET
 253  
                         && socket.getReceiveBufferSize() != tcpConnector.getReceiveBufferSize())
 254  
                 {
 255  0
                     socket.setReceiveBufferSize(tcpConnector.getReceiveBufferSize());
 256  
                 }
 257  2687
                 if (tcpConnector.getSendBufferSize() != UMOConnector.INT_VALUE_NOT_SET
 258  
                         && socket.getSendBufferSize() != tcpConnector.getSendBufferSize())
 259  
                 {
 260  4
                     socket.setSendBufferSize(tcpConnector.getSendBufferSize());
 261  
                 }
 262  2687
                 if (tcpConnector.getReceiveTimeout() != UMOConnector.INT_VALUE_NOT_SET
 263  
                         && socket.getSoTimeout() != tcpConnector.getReceiveTimeout())
 264  
                 {
 265  20
                     socket.setSoTimeout(tcpConnector.getReceiveTimeout());
 266  
                 }
 267  
 
 268  
                 try
 269  
                 {
 270  2687
                     socket.setTcpNoDelay(tcpConnector.isSendTcpNoDelay());
 271  
                 }
 272  0
                 catch (SocketException e)
 273  
                 {
 274  
                     // MULE-2800 - Solaris may throw an exception here that we want to ignore
 275  2687
                 }
 276  2687
                 socket.setKeepAlive(tcpConnector.isKeepAlive());
 277  
 
 278  2687
                 dataIn = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
 279  2687
                 dataOut = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
 280  
 
 281  
             }
 282  0
             catch (IOException e)
 283  
             {
 284  0
                 logger.error("Failed to set Socket properties: " + e.getMessage(), e);
 285  2687
             }
 286  
 
 287  2687
         }
 288  
 
 289  
         public void dispose()
 290  
         {
 291  0
             release();
 292  0
         }
 293  
 
 294  
         public void release()
 295  
         {
 296  2667
             closed.set(true);
 297  
             try
 298  
             {
 299  2667
                 if (socket != null && !socket.isClosed())
 300  
                 {
 301  2667
                     if (logger.isDebugEnabled())
 302  
                     {
 303  
                         // some dirty workaround for IBM JSSE's SSL implementation,
 304  
                         // which closes sockets asynchronously by that point.
 305  0
                         final SocketAddress socketAddress = socket.getLocalSocketAddress();
 306  0
                         if (socketAddress == null)
 307  
                         {
 308  0
                             logger.debug("Listener has already been closed by other process.");
 309  
                         }
 310  
                         else
 311  
                         {
 312  0
                             logger.debug("Closing listener: " + socketAddress);
 313  
                         }
 314  
                     }
 315  2667
                     socket.close();
 316  
                 }
 317  
             }
 318  0
             catch (IOException e)
 319  
             {
 320  0
                 logger.warn("Socket close failed with: " + e);
 321  2667
             }
 322  2667
         }
 323  
 
 324  
         protected void bindTransaction(UMOTransaction tx) throws TransactionException
 325  
         {
 326  
             //nothing to do
 327  0
         }
 328  
 
 329  
         protected Object getNextMessage(Object resource) throws Exception
 330  
         {
 331  5309
             while (!socket.isClosed() && !disposing.get())
 332  
             {
 333  
                 try
 334  
                 {
 335  5304
                     Object readMsg = protocol.read(dataIn);
 336  5304
                     if (readMsg == null)
 337  
                     {
 338  2662
                         return null;
 339  
                     }
 340  
                     else
 341  
                     {
 342  2642
                         return readMsg;
 343  
                     }
 344  
                 }
 345  0
                 catch (SocketTimeoutException e)
 346  
                 {
 347  0
                     if (!socket.getKeepAlive())
 348  
                     {
 349  0
                         return null;
 350  
                     }
 351  0
                 }
 352  
             }
 353  5
             return null;
 354  
         }
 355  
 
 356  
         //@Override
 357  
         protected void handleResults(List messages) throws Exception
 358  
         {
 359  
             //should send back only if remote synch is set or no outbound endpoints
 360  2642
             if (endpoint.isRemoteSync() || !component.getDescriptor().getOutboundRouter().hasEndpoints())
 361  
             {
 362  224
                 for (Iterator iterator = messages.iterator(); iterator.hasNext();)
 363  
                 {
 364  224
                     Object o = iterator.next();
 365  224
                     protocol.write(dataOut, o);
 366  224
                     dataOut.flush();
 367  224
                 }
 368  
             }
 369  2642
         }
 370  
 
 371  
         protected void preRouteMuleMessage(final MuleMessage message) throws Exception
 372  
         {
 373  193674
             super.preRouteMuleMessage(message);
 374  
 
 375  193674
             final SocketAddress clientAddress = socket.getRemoteSocketAddress();
 376  193673
             if (clientAddress != null)
 377  
             {
 378  193673
                 message.setProperty(MuleProperties.MULE_REMOTE_CLIENT_ADDRESS, clientAddress.toString());
 379  
             }
 380  193673
         }
 381  
     }
 382  
 
 383  
     protected class TcpStreamWorker extends TcpWorker
 384  
     {
 385  
 
 386  
         private CountDownLatch latch;
 387  
 
 388  
         public TcpStreamWorker(Socket socket, AbstractMessageReceiver receiver) throws IOException
 389  20
         {
 390  20
             super(socket, receiver);
 391  20
         }
 392  
 
 393  
         //Override
 394  
         protected Object getNextMessage(Object resource) throws Exception
 395  
         {
 396  191032
             latch = new CountDownLatch(2);
 397  191032
             dataIn = new CloseCountDownInputStream(dataIn, latch);
 398  
             
 399  
             //all we can do for streaming is connect the streams
 400  191032
             if (endpoint.isSynchronous())
 401  
             {
 402  16018
                 dataOut = new CloseCountDownOutputStream(dataOut, latch);
 403  
             }
 404  
 
 405  191032
             UMOMessageAdapter adapter = connector.getStreamMessageAdapter(dataIn, dataOut);
 406  191032
             return adapter;
 407  
 
 408  
         }
 409  
 
 410  
         //@Override
 411  
         protected void handleResults(List messages) throws Exception
 412  
         {
 413  20
             latch.await();
 414  0
         }
 415  
     }
 416  
 
 417  
 }