View Javadoc

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  public class TcpMessageReceiver extends AbstractMessageReceiver implements Work
62  {
63      private ServerSocket serverSocket = null;
64  
65      public TcpMessageReceiver(UMOConnector connector, UMOComponent component, UMOEndpoint endpoint)
66              throws InitialisationException
67      {
68          super(connector, component, endpoint);
69      }
70  
71      protected void doConnect() throws ConnectException
72      {
73          disposing.set(false);
74  
75          URI uri = endpoint.getEndpointURI().getUri();
76  
77          try
78          {
79              serverSocket = ((TcpConnector) connector).getServerSocket(uri);
80          }
81          catch (Exception e)
82          {
83              throw new org.mule.providers.ConnectException(TcpMessages.failedToBindToUri(uri), e, this);
84          }
85  
86          try
87          {
88              getWorkManager().scheduleWork(this, WorkManager.INDEFINITE, null, connector);
89          }
90          catch (WorkException e)
91          {
92              throw new ConnectException(CoreMessages.failedToScheduleWork(), e, this);
93          }
94      }
95  
96      protected void doDisconnect() throws ConnectException
97      {
98          // this will cause the server thread to quit
99          disposing.set(true);
100 
101         try
102         {
103             if (serverSocket != null)
104             {
105                 if (logger.isDebugEnabled())
106                 {
107                     logger.debug("Closing: " + serverSocket);
108                 }
109                 serverSocket.close();
110             }
111         }
112         catch (IOException e)
113         {
114             logger.warn("Failed to close server socket: " + e.getMessage(), e);
115         }
116     }
117 
118     protected void doStart() throws UMOException
119     {
120         // nothing to do
121     }
122 
123     protected void doStop() throws UMOException
124     {
125         // nothing to do
126     }
127 
128     /**
129      * Obtain the serverSocket
130      *
131      * @return the server socket for this server
132      */
133     public ServerSocket getServerSocket()
134     {
135         return serverSocket;
136     }
137 
138     public void run()
139     {
140         while (!disposing.get())
141         {
142             if (connector.isStarted() && !disposing.get())
143             {
144                 Socket socket = null;
145                 try
146                 {
147                     socket = serverSocket.accept();
148 
149                     if (logger.isTraceEnabled())
150                     {
151                         logger.trace("Accepted: " + serverSocket);
152                     }
153                 }
154                 catch (java.io.InterruptedIOException iie)
155                 {
156                     if (logger.isDebugEnabled())
157                     {
158                         logger.debug("Interupted IO doing serverSocket.accept: " + iie.getMessage());
159                     }
160                 }
161                 catch (Exception e)
162                 {
163                     if (!connector.isDisposed() && !disposing.get())
164                     {
165                         logger.warn("Accept failed on socket: " + e, e);
166                         handleException(new ConnectException(e, this));
167                     }
168                 }
169 
170                 if (socket != null)
171                 {
172                     try
173                     {
174                         Work work = createWork(socket);
175                         try
176                         {
177                             getWorkManager().scheduleWork(work, WorkManager.INDEFINITE, null, connector);
178                         }
179                         catch (WorkException e)
180                         {
181                             logger.error("Tcp Server receiver Work was not processed: " + e.getMessage(), e);
182                         }
183                     }
184                     catch (IOException e)
185                     {
186                         handleException(e);
187                     }
188                 }
189             }
190         }
191     }
192 
193     public void release()
194     {
195         // template method
196     }
197 
198     protected void doDispose()
199     {
200         try
201         {
202             if (serverSocket != null && !serverSocket.isClosed())
203             {
204                 if (logger.isDebugEnabled())
205                 {
206                     logger.debug("Closing: " + serverSocket);
207                 }
208                 serverSocket.close();
209             }
210             serverSocket = null;
211         }
212         catch (Exception e)
213         {
214             logger.error(new DisposeException(TcpMessages.failedToCloseSocket(), e));
215         }
216         logger.info("Closed Tcp port");
217     }
218 
219     protected Work createWork(Socket socket) throws IOException
220     {
221         if (endpoint.isStreaming())
222         {
223             return new TcpStreamWorker(socket, this);
224         }
225         else
226         {
227             return new TcpWorker(socket, this);
228         }
229     }
230 
231     protected class TcpWorker extends AbstractReceiverResourceWorker implements Disposable
232     {
233         protected Socket socket = null;
234         protected InputStream dataIn;
235         protected OutputStream dataOut;
236         protected AtomicBoolean closed = new AtomicBoolean(false);
237         protected TcpProtocol protocol;
238 
239         public TcpWorker(Object resource, AbstractMessageReceiver receiver) throws IOException
240         {
241             super(resource, receiver, new ResponseOutputStream((Socket) resource));
242 
243             this.socket = (Socket) resource;
244 
245             final TcpConnector tcpConnector = ((TcpConnector) connector);
246             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                 if (tcpConnector.getReceiveBufferSize() != UMOConnector.INT_VALUE_NOT_SET
253                         && socket.getReceiveBufferSize() != tcpConnector.getReceiveBufferSize())
254                 {
255                     socket.setReceiveBufferSize(tcpConnector.getReceiveBufferSize());
256                 }
257                 if (tcpConnector.getSendBufferSize() != UMOConnector.INT_VALUE_NOT_SET
258                         && socket.getSendBufferSize() != tcpConnector.getSendBufferSize())
259                 {
260                     socket.setSendBufferSize(tcpConnector.getSendBufferSize());
261                 }
262                 if (tcpConnector.getReceiveTimeout() != UMOConnector.INT_VALUE_NOT_SET
263                         && socket.getSoTimeout() != tcpConnector.getReceiveTimeout())
264                 {
265                     socket.setSoTimeout(tcpConnector.getReceiveTimeout());
266                 }
267 
268                 try
269                 {
270                     socket.setTcpNoDelay(tcpConnector.isSendTcpNoDelay());
271                 }
272                 catch (SocketException e)
273                 {
274                     // MULE-2800 - Solaris may throw an exception here that we want to ignore
275                 }
276                 socket.setKeepAlive(tcpConnector.isKeepAlive());
277 
278                 dataIn = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
279                 dataOut = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
280 
281             }
282             catch (IOException e)
283             {
284                 logger.error("Failed to set Socket properties: " + e.getMessage(), e);
285             }
286 
287         }
288 
289         public void dispose()
290         {
291             release();
292         }
293 
294         public void release()
295         {
296             closed.set(true);
297             try
298             {
299                 if (socket != null && !socket.isClosed())
300                 {
301                     if (logger.isDebugEnabled())
302                     {
303                         // some dirty workaround for IBM JSSE's SSL implementation,
304                         // which closes sockets asynchronously by that point.
305                         final SocketAddress socketAddress = socket.getLocalSocketAddress();
306                         if (socketAddress == null)
307                         {
308                             logger.debug("Listener has already been closed by other process.");
309                         }
310                         else
311                         {
312                             logger.debug("Closing listener: " + socketAddress);
313                         }
314                     }
315                     socket.close();
316                 }
317             }
318             catch (IOException e)
319             {
320                 logger.warn("Socket close failed with: " + e);
321             }
322         }
323 
324         protected void bindTransaction(UMOTransaction tx) throws TransactionException
325         {
326             //nothing to do
327         }
328 
329         protected Object getNextMessage(Object resource) throws Exception
330         {
331             while (!socket.isClosed() && !disposing.get())
332             {
333                 try
334                 {
335                     Object readMsg = protocol.read(dataIn);
336                     if (readMsg == null)
337                     {
338                         return null;
339                     }
340                     else
341                     {
342                         return readMsg;
343                     }
344                 }
345                 catch (SocketTimeoutException e)
346                 {
347                     if (!socket.getKeepAlive())
348                     {
349                         return null;
350                     }
351                 }
352             }
353             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             if (endpoint.isRemoteSync() || !component.getDescriptor().getOutboundRouter().hasEndpoints())
361             {
362                 for (Iterator iterator = messages.iterator(); iterator.hasNext();)
363                 {
364                     Object o = iterator.next();
365                     protocol.write(dataOut, o);
366                     dataOut.flush();
367                 }
368             }
369         }
370 
371         protected void preRouteMuleMessage(final MuleMessage message) throws Exception
372         {
373             super.preRouteMuleMessage(message);
374 
375             final SocketAddress clientAddress = socket.getRemoteSocketAddress();
376             if (clientAddress != null)
377             {
378                 message.setProperty(MuleProperties.MULE_REMOTE_CLIENT_ADDRESS, clientAddress.toString());
379             }
380         }
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         {
390             super(socket, receiver);
391         }
392 
393         //Override
394         protected Object getNextMessage(Object resource) throws Exception
395         {
396             latch = new CountDownLatch(2);
397             dataIn = new CloseCountDownInputStream(dataIn, latch);
398             
399             //all we can do for streaming is connect the streams
400             if (endpoint.isSynchronous())
401             {
402                 dataOut = new CloseCountDownOutputStream(dataOut, latch);
403             }
404 
405             UMOMessageAdapter adapter = connector.getStreamMessageAdapter(dataIn, dataOut);
406             return adapter;
407 
408         }
409 
410         //@Override
411         protected void handleResults(List messages) throws Exception
412         {
413             latch.await();
414         }
415     }
416 
417 }