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