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