View Javadoc

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