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