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