1
2
3
4
5
6
7
8
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
54
55
56 public class TcpMessageReceiver extends AbstractMessageReceiver implements Work
57 {
58 private ServerSocket serverSocket = null;
59
60 protected final AtomicBoolean disposing = new AtomicBoolean(false);
61
62 public TcpMessageReceiver(Connector connector, FlowConstruct flowConstruct, InboundEndpoint endpoint)
63 throws CreateException
64 {
65 super(connector, flowConstruct, endpoint);
66 }
67
68 @Override
69 protected void doConnect() throws ConnectException
70 {
71 disposing.set(false);
72
73 URI uri = endpoint.getEndpointURI().getUri();
74
75 try
76 {
77 serverSocket = ((TcpConnector) connector).getServerSocket(uri);
78 }
79 catch (Exception e)
80 {
81 throw new ConnectException(TcpMessages.failedToBindToUri(uri), e, this);
82 }
83
84 try
85 {
86 getWorkManager().scheduleWork(this, WorkManager.INDEFINITE, null, connector);
87 }
88 catch (WorkException e)
89 {
90 throw new ConnectException(CoreMessages.failedToScheduleWork(), e, this);
91 }
92 }
93
94 @Override
95 protected void doDisconnect() throws ConnectException
96 {
97
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 @Override
118 protected void doStart() throws MuleException
119 {
120
121 }
122
123 @Override
124 protected void doStop() throws MuleException
125 {
126
127 }
128
129
130
131
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 try
145 {
146 retryTemplate.execute(new RetryCallback()
147 {
148 public void doWork(RetryContext context) throws Exception
149 {
150 Socket socket = null;
151 try
152 {
153 socket = serverSocket.accept();
154 }
155 catch (Exception e)
156 {
157 if (!connector.isDisposed() && !disposing.get())
158 {
159 throw new ConnectException(e, null);
160 }
161 }
162
163 if (socket != null)
164 {
165 Work work = createWork(socket);
166 getWorkManager().scheduleWork(work, WorkManager.INDEFINITE, null, connector);
167 }
168 }
169
170 public String getWorkDescription()
171 {
172 return getConnectionDescription();
173 }
174 }, getWorkManager());
175 }
176 catch (Exception e)
177 {
178 getConnector().getMuleContext().getExceptionListener().handleException(e);
179 }
180 }
181 }
182 }
183
184 public void release()
185 {
186
187 }
188
189 @Override
190 protected void doDispose()
191 {
192 try
193 {
194 if (serverSocket != null && !serverSocket.isClosed())
195 {
196 if (logger.isDebugEnabled())
197 {
198 logger.debug("Closing: " + serverSocket);
199 }
200 serverSocket.close();
201 }
202 serverSocket = null;
203 }
204 catch (Exception e)
205 {
206 logger.error(new DisposeException(TcpMessages.failedToCloseSocket(), e, this));
207 }
208 logger.info("Closed Tcp port");
209 }
210
211 protected Work createWork(Socket socket) throws IOException
212 {
213 return new TcpWorker(socket, this);
214 }
215
216 protected class TcpWorker extends AbstractReceiverResourceWorker implements Disposable, Expirable
217 {
218 protected Socket socket = null;
219 protected TcpInputStream dataIn;
220 protected InputStream underlyingIn;
221 protected OutputStream dataOut;
222 protected TcpProtocol protocol;
223 protected boolean dataInWorkFinished = false;
224 protected Object notify = new Object();
225 private boolean moreMessages = true;
226
227 public TcpWorker(Socket socket, AbstractMessageReceiver receiver) throws IOException
228 {
229 super(socket, receiver, ((TcpConnector) connector).getTcpProtocol().createResponse(socket));
230 this.socket = socket;
231
232 final TcpConnector tcpConnector = ((TcpConnector) connector);
233 protocol = tcpConnector.getTcpProtocol();
234
235 try
236 {
237 tcpConnector.configureSocket(TcpConnector.SERVER, socket);
238
239 underlyingIn = new BufferedInputStream(socket.getInputStream());
240 dataIn = new TcpInputStream(underlyingIn)
241 {
242 @Override
243 public void close() throws IOException
244 {
245
246
247
248 dataInWorkFinished = true;
249 moreMessages = false;
250
251 synchronized (notify)
252 {
253 notify.notifyAll();
254 }
255 }
256 };
257 dataOut = new BufferedOutputStream(socket.getOutputStream());
258 }
259 catch (IOException e)
260 {
261 logger.error("Failed to set Socket properties: " + e.getMessage(), e);
262 }
263 }
264
265 public void expired()
266 {
267 dispose();
268 }
269
270 public void dispose()
271 {
272 releaseSocket();
273 }
274
275 @Override
276 public void release()
277 {
278 waitForStreams();
279 releaseSocket();
280 }
281
282 private void waitForStreams()
283 {
284
285
286
287 if (!dataInWorkFinished)
288 {
289 synchronized (notify)
290 {
291 if (!dataInWorkFinished)
292 {
293 try
294 {
295 notify.wait();
296 }
297 catch (InterruptedException e)
298 {
299 }
300 }
301 }
302 }
303 }
304
305
306
307
308 private void releaseSocket()
309 {
310 try
311 {
312 if (socket != null && !socket.isClosed())
313 {
314 if (logger.isDebugEnabled())
315 {
316
317
318 final SocketAddress socketAddress = socket.getLocalSocketAddress();
319 if (socketAddress == null)
320 {
321 logger.debug("Listener has already been closed by other process.");
322 }
323 else
324 {
325 logger.debug("Closing listener: " + socketAddress);
326 }
327 }
328
329 shutdownSocket();
330 socket.close();
331 }
332 }
333 catch (IOException e)
334 {
335 logger.warn("Socket close failed with: " + e);
336 }
337 }
338
339 protected void shutdownSocket() throws IOException
340 {
341 try
342 {
343 socket.shutdownOutput();
344 }
345 catch (UnsupportedOperationException e)
346 {
347
348 }
349 }
350
351 @Override
352 protected void bindTransaction(Transaction tx) throws TransactionException
353 {
354
355 }
356
357 @Override
358 protected Object getNextMessage(Object resource) throws Exception
359 {
360 long keepAliveTimeout = ((TcpConnector)connector).getKeepAliveTimeout();
361
362 Object readMsg = null;
363 try
364 {
365
366 if(keepAliveTimeout > 0)
367 {
368 ((TcpConnector) connector).getKeepAliveMonitor().addExpirable(keepAliveTimeout,
369 TimeUnit.MILLISECONDS, this);
370 }
371
372 readMsg = protocol.read(dataIn);
373
374
375 ((TcpConnector) connector).getKeepAliveMonitor().removeExpirable(this);
376
377 if (dataIn.isStreaming())
378 {
379 moreMessages = false;
380 }
381
382 return readMsg;
383 }
384 catch (SocketTimeoutException e)
385 {
386 ((TcpConnector) connector).getKeepAliveMonitor().removeExpirable(this);
387 }
388 finally
389 {
390 if (readMsg == null)
391 {
392
393
394
395 dataIn.close();
396 }
397 }
398
399 return null;
400 }
401
402 @Override
403 protected boolean hasMoreMessages(Object message)
404 {
405 return !socket.isClosed() && !dataInWorkFinished
406 && !disposing.get() && moreMessages;
407 }
408
409 @Override
410 protected void handleResults(List messages) throws Exception
411 {
412
413 if (endpoint.getExchangePattern().hasResponse())
414 {
415 for (Iterator iterator = messages.iterator(); iterator.hasNext();)
416 {
417 Object o = iterator.next();
418 protocol.write(dataOut, o);
419 dataOut.flush();
420 }
421 }
422 }
423
424 @Override
425 protected void preRouteMuleMessage(final MuleMessage message) throws Exception
426 {
427 super.preRouteMuleMessage(message);
428
429 final SocketAddress clientAddress = socket.getRemoteSocketAddress();
430 if (clientAddress != null)
431 {
432 message.setOutboundProperty(MuleProperties.MULE_REMOTE_CLIENT_ADDRESS, clientAddress.toString());
433 }
434 }
435 }
436
437 }