Coverage Report - org.mule.transport.tcp.TcpMessageReceiver
 
Classes in this File Line Coverage Branch Coverage Complexity
TcpMessageReceiver
62%
40/64
54%
14/26
2.609
TcpMessageReceiver$TcpWorker
77%
59/77
74%
25/34
2.609
TcpMessageReceiver$TcpWorker$1
100%
7/7
N/A
2.609
 
 1  
 /*
 2  
  * $Id: TcpMessageReceiver.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  
 package org.mule.transport.tcp;
 11  
 
 12  
 import org.mule.DefaultMuleMessage;
 13  
 import org.mule.api.MuleException;
 14  
 import org.mule.api.config.MuleProperties;
 15  
 import org.mule.api.endpoint.InboundEndpoint;
 16  
 import org.mule.api.lifecycle.CreateException;
 17  
 import org.mule.api.lifecycle.Disposable;
 18  
 import org.mule.api.lifecycle.DisposeException;
 19  
 import org.mule.api.service.Service;
 20  
 import org.mule.api.transaction.Transaction;
 21  
 import org.mule.api.transaction.TransactionException;
 22  
 import org.mule.api.transport.Connector;
 23  
 import org.mule.config.i18n.CoreMessages;
 24  
 import org.mule.transport.AbstractMessageReceiver;
 25  
 import org.mule.transport.AbstractReceiverResourceWorker;
 26  
 import org.mule.transport.ConnectException;
 27  
 import org.mule.transport.tcp.i18n.TcpMessages;
 28  
 import org.mule.util.monitor.Expirable;
 29  
 
 30  
 import java.io.BufferedInputStream;
 31  
 import java.io.BufferedOutputStream;
 32  
 import java.io.IOException;
 33  
 import java.io.InputStream;
 34  
 import java.io.OutputStream;
 35  
 import java.net.ServerSocket;
 36  
 import java.net.Socket;
 37  
 import java.net.SocketAddress;
 38  
 import java.net.SocketTimeoutException;
 39  
 import java.net.URI;
 40  
 import java.util.Iterator;
 41  
 import java.util.List;
 42  
 
 43  
 import javax.resource.spi.work.Work;
 44  
 import javax.resource.spi.work.WorkException;
 45  
 import javax.resource.spi.work.WorkManager;
 46  
 
 47  
 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
 48  
 
 49  
 /**
 50  
  * <code>TcpMessageReceiver</code> acts like a TCP server to receive socket
 51  
  * requests.
 52  
  */
 53  15452
 public class TcpMessageReceiver extends AbstractMessageReceiver implements Work
 54  
 {
 55  118
     private ServerSocket serverSocket = null;
 56  
 
 57  
     public TcpMessageReceiver(Connector connector, Service service, InboundEndpoint endpoint)
 58  
             throws CreateException
 59  
     {
 60  118
         super(connector, service, endpoint);
 61  118
     }
 62  
 
 63  
     protected void doConnect() throws ConnectException
 64  
     {
 65  114
         disposing.set(false);
 66  
 
 67  114
         URI uri = endpoint.getEndpointURI().getUri();
 68  
 
 69  
         try
 70  
         {
 71  114
             serverSocket = ((TcpConnector) connector).getServerSocket(uri);
 72  
         }
 73  0
         catch (Exception e)
 74  
         {
 75  0
             throw new org.mule.transport.ConnectException(TcpMessages.failedToBindToUri(uri), e, this);
 76  114
         }
 77  
 
 78  
         try
 79  
         {
 80  114
             getWorkManager().scheduleWork(this, WorkManager.INDEFINITE, null, connector);
 81  
         }
 82  0
         catch (WorkException e)
 83  
         {
 84  0
             throw new ConnectException(CoreMessages.failedToScheduleWork(), e, this);
 85  114
         }
 86  114
     }
 87  
 
 88  
     protected void doDisconnect() throws ConnectException
 89  
     {
 90  
         // this will cause the server thread to quit
 91  114
         disposing.set(true);
 92  
 
 93  
         try
 94  
         {
 95  114
             if (serverSocket != null)
 96  
             {
 97  114
                 if (logger.isDebugEnabled())
 98  
                 {
 99  0
                     logger.debug("Closing: " + serverSocket);
 100  
                 }
 101  114
                 serverSocket.close();
 102  
             }
 103  
         }
 104  0
         catch (IOException e)
 105  
         {
 106  0
             logger.warn("Failed to close server socket: " + e.getMessage(), e);
 107  114
         }
 108  114
     }
 109  
 
 110  
     protected void doStart() throws MuleException
 111  
     {
 112  
         // nothing to do
 113  114
     }
 114  
 
 115  
     protected void doStop() throws MuleException
 116  
     {
 117  
         // nothing to do
 118  114
     }
 119  
 
 120  
     /**
 121  
      * Obtain the serverSocket
 122  
      * @return the server socket for this server
 123  
      */
 124  
     public ServerSocket getServerSocket()
 125  
     {
 126  0
         return serverSocket;
 127  
     }
 128  
 
 129  
     public void run()
 130  
     {
 131  1684
         while (!disposing.get())
 132  
         {
 133  1570
             if (connector.isStarted() && !disposing.get())
 134  
             {
 135  1570
                 Socket socket = null;
 136  
                 try
 137  
                 {
 138  1570
                     socket = serverSocket.accept();
 139  
 
 140  1462
                     if (logger.isTraceEnabled())
 141  
                     {
 142  0
                         logger.trace("Accepted: " + serverSocket);
 143  
                     }
 144  
                 }
 145  0
                 catch (java.io.InterruptedIOException iie)
 146  
                 {
 147  0
                     if (logger.isDebugEnabled())
 148  
                     {
 149  0
                         logger.debug("Interupted IO doing serverSocket.accept: " + iie.getMessage());
 150  
                     }
 151  
                 }
 152  108
                 catch (Exception e)
 153  
                 {
 154  108
                     if (!connector.isDisposed() && !disposing.get())
 155  
                     {
 156  0
                         logger.warn("Accept failed on socket: " + e, e);
 157  0
                         handleException(new ConnectException(e, this));
 158  
                     }
 159  1462
                 }
 160  
 
 161  1570
                 if (socket != null)
 162  
                 {
 163  
                     try
 164  
                     {
 165  1462
                         Work work = createWork(socket);
 166  
                         try
 167  
                         {
 168  1462
                             getWorkManager().scheduleWork(work, WorkManager.INDEFINITE, null, connector);
 169  
                         }
 170  0
                         catch (WorkException e)
 171  
                         {
 172  0
                             logger.error("Tcp Server receiver Work was not processed: " + e.getMessage(), e);
 173  1462
                         }
 174  
                     }
 175  0
                     catch (IOException e)
 176  
                     {
 177  0
                         handleException(e);
 178  1462
                     }
 179  
                 }
 180  1570
             }
 181  
         }
 182  114
     }
 183  
 
 184  
     public void release()
 185  
     {
 186  
         // template method
 187  0
     }
 188  
 
 189  
     protected void doDispose()
 190  
     {
 191  
         try
 192  
         {
 193  234
             if (serverSocket != null && !serverSocket.isClosed())
 194  
             {
 195  0
                 if (logger.isDebugEnabled())
 196  
                 {
 197  0
                     logger.debug("Closing: " + serverSocket);
 198  
                 }
 199  0
                 serverSocket.close();
 200  
             }
 201  234
             serverSocket = null;
 202  
         }
 203  0
         catch (Exception e)
 204  
         {
 205  0
             logger.error(new DisposeException(TcpMessages.failedToCloseSocket(), e, this));
 206  234
         }
 207  234
         logger.info("Closed Tcp port");
 208  234
     }
 209  
 
 210  
     protected Work createWork(Socket socket) throws IOException
 211  
     {
 212  1462
         return new TcpWorker(socket, this);
 213  
     }
 214  
 
 215  1425
     protected class TcpWorker extends AbstractReceiverResourceWorker implements Disposable, Expirable
 216  
     {
 217  1462
         protected Socket socket = null;
 218  
         protected TcpInputStream dataIn;
 219  
         protected InputStream underlyingIn;
 220  
         protected OutputStream dataOut;
 221  
         protected TcpProtocol protocol;
 222  1462
         protected boolean dataInWorkFinished = false;
 223  1462
         protected Object notify = new Object();
 224  1462
         private boolean moreMessages = true;
 225  
         
 226  
         public TcpWorker(Socket socket, AbstractMessageReceiver receiver) throws IOException
 227  1462
         {
 228  1462
             super(socket, receiver, ((TcpConnector) connector).getTcpProtocol().createResponse(socket));
 229  1462
             this.socket = socket;
 230  
 
 231  1462
             final TcpConnector tcpConnector = ((TcpConnector) connector);
 232  1462
             protocol = tcpConnector.getTcpProtocol();
 233  
 
 234  
             try
 235  
             {
 236  1462
                 tcpConnector.configureSocket(TcpConnector.SERVER, socket);
 237  
 
 238  1462
                 underlyingIn = new BufferedInputStream(socket.getInputStream());
 239  1462
                 dataIn = new TcpInputStream(underlyingIn)
 240  
                 {
 241  1462
                     public void close() throws IOException
 242  
                     {
 243  
                         // Don't actually close the stream, we just want to know if the
 244  
                         // we want to stop receiving messages on this sockete.
 245  
                         // The Protocol is responsible for closing this.
 246  1425
                         dataInWorkFinished = true;
 247  1425
                         moreMessages = false;
 248  
                         
 249  1425
                         synchronized (notify)
 250  
                         {
 251  1425
                             notify.notifyAll();
 252  1425
                         }
 253  1425
                     }
 254  
                 };
 255  1462
                 dataOut = new BufferedOutputStream(socket.getOutputStream());
 256  
             }
 257  0
             catch (IOException e)
 258  
             {
 259  0
                 logger.error("Failed to set Socket properties: " + e.getMessage(), e);
 260  1462
             }
 261  1462
         }
 262  
 
 263  
         public void expired()
 264  
         {
 265  0
             dispose();
 266  0
         }
 267  
         
 268  
         public void dispose()
 269  
         {
 270  0
             releaseSocket();
 271  0
         }
 272  
 
 273  
         public void release()
 274  
         {
 275  1462
                 waitForStreams();
 276  1462
             releaseSocket();
 277  1462
         }
 278  
 
 279  
         private void waitForStreams()
 280  
         {
 281  
             // The Message with the InputStream as a payload can be dispatched
 282  
             // into a different thread, in which case we need to wait for it to 
 283  
             // finish streaming 
 284  1462
             if (!dataInWorkFinished)
 285  
             {
 286  39
                 synchronized (notify)
 287  
                 {
 288  39
                     if (!dataInWorkFinished)
 289  
                     {
 290  
                         try
 291  
                         {
 292  39
                             notify.wait();
 293  
                         }
 294  37
                         catch (InterruptedException e)
 295  
                         {
 296  2
                         }
 297  
                     }
 298  39
                 }
 299  
             }
 300  1462
         }
 301  
 
 302  
         /**
 303  
          * Releases the socket when the input stream is closed.
 304  
          */
 305  
         private void releaseSocket()
 306  
         {
 307  
             try
 308  
             {
 309  1462
                 if (socket != null && !socket.isClosed())
 310  
                 {
 311  1462
                     if (logger.isDebugEnabled())
 312  
                     {
 313  
                         // some dirty workaround for IBM JSSE's SSL implementation,
 314  
                         // which closes sockets asynchronously by that point.
 315  0
                         final SocketAddress socketAddress = socket.getLocalSocketAddress();
 316  0
                         if (socketAddress == null)
 317  
                         {
 318  0
                             logger.debug("Listener has already been closed by other process.");
 319  
                         }
 320  
                         else
 321  
                         {
 322  0
                             logger.debug("Closing listener: " + socketAddress);
 323  
                         }
 324  
                     }
 325  
                     
 326  1462
                     shutdownSocket();
 327  1462
                     socket.close();
 328  
                 }
 329  
             }
 330  0
             catch (IOException e)
 331  
             {
 332  0
                 logger.warn("Socket close failed with: " + e);
 333  1462
             }
 334  1462
         }
 335  
 
 336  
         protected void shutdownSocket() throws IOException
 337  
         {
 338  
             try
 339  
             {
 340  1462
                 socket.shutdownOutput();
 341  
             }
 342  0
             catch (UnsupportedOperationException e)
 343  
             {
 344  
                 //Ignore, not supported by ssl sockets
 345  1462
             }
 346  1462
         }
 347  
 
 348  
         protected void bindTransaction(Transaction tx) throws TransactionException
 349  
         {
 350  
             //nothing to do
 351  0
         }
 352  
 
 353  
         protected Object getNextMessage(Object resource) throws Exception
 354  
         {
 355  3129
             long keepAliveTimeout = ((TcpConnector)connector).getKeepAliveTimeout();
 356  
             
 357  3129
                 Object readMsg = null;
 358  
             try
 359  
             {
 360  
                 // Create a monitor if expiry was set
 361  3129
                 if(keepAliveTimeout > 0)
 362  
                 {
 363  0
                     ((TcpConnector) connector).getKeepAliveMonitor().addExpirable(keepAliveTimeout, 
 364  
                         TimeUnit.MILLISECONDS, this);
 365  
                 }
 366  
                 
 367  3129
                 readMsg = protocol.read(dataIn);
 368  
                 
 369  
                 // There was some action so we can clear the monitor
 370  3122
                 ((TcpConnector) connector).getKeepAliveMonitor().removeExpirable(this);
 371  
                 
 372  3122
                 if (dataIn.isStreaming())
 373  
                 {
 374  20
                     moreMessages = false;
 375  
                 } 
 376  
                 
 377  3122
                 return readMsg;
 378  
             }
 379  0
             catch (SocketTimeoutException e)
 380  
             {
 381  0
                 ((TcpConnector) connector).getKeepAliveMonitor().removeExpirable(this);
 382  
             }
 383  
             finally
 384  
             {
 385  3129
                     if (readMsg == null)
 386  
                     {
 387  
                             // Protocols can return a null object, which means we're done
 388  
                     // reading messages for now and can mark the stream for closing later.
 389  
                             // Also, exceptions can be thrown, in which case we're done reading.
 390  1415
                     dataIn.close();
 391  
                     }
 392  
             }
 393  
             
 394  0
             return null;
 395  
         }
 396  
         
 397  
         protected boolean hasMoreMessages(Object message)
 398  
         {
 399  1714
             return !socket.isClosed() && !dataInWorkFinished 
 400  
                 && !disposing.get() && moreMessages;
 401  
         }
 402  
 
 403  
         //@Override
 404  
         protected void handleResults(List messages) throws Exception
 405  
         {            
 406  
             //should send back only if remote synch is set or no outbound endpoints
 407  3121
             if (endpoint.isRemoteSync() || !service.getOutboundRouter().hasEndpoints())
 408  
             {
 409  135
                 for (Iterator iterator = messages.iterator(); iterator.hasNext();)
 410  
                 {
 411  52
                     Object o = iterator.next();
 412  52
                     protocol.write(dataOut, o);
 413  31
                     dataOut.flush();
 414  31
                 }
 415  
             }
 416  3100
         }
 417  
 
 418  
         protected void preRouteMuleMessage(final DefaultMuleMessage message) throws Exception
 419  
         {
 420  1714
             super.preRouteMuleMessage(message);
 421  
 
 422  1714
             final SocketAddress clientAddress = socket.getRemoteSocketAddress();
 423  1714
             if (clientAddress != null)
 424  
             {
 425  1714
                 message.setProperty(MuleProperties.MULE_REMOTE_CLIENT_ADDRESS, clientAddress.toString());
 426  
             }
 427  1714
         }
 428  
     }
 429  
 
 430  
 }