1
2
3
4
5
6
7
8
9
10 package org.mule.transport.tcp;
11
12 import org.mule.DefaultMuleMessage;
13 import org.mule.api.MuleException;
14 import org.mule.api.config.MuleProperties;
15 import org.mule.api.endpoint.InboundEndpoint;
16 import org.mule.api.lifecycle.CreateException;
17 import org.mule.api.lifecycle.Disposable;
18 import org.mule.api.lifecycle.DisposeException;
19 import org.mule.api.service.Service;
20 import org.mule.api.transaction.Transaction;
21 import org.mule.api.transaction.TransactionException;
22 import org.mule.api.transport.Connector;
23 import org.mule.config.i18n.CoreMessages;
24 import org.mule.transport.AbstractMessageReceiver;
25 import org.mule.transport.AbstractReceiverResourceWorker;
26 import org.mule.transport.ConnectException;
27 import org.mule.transport.tcp.i18n.TcpMessages;
28 import org.mule.util.monitor.Expirable;
29
30 import java.io.BufferedInputStream;
31 import java.io.BufferedOutputStream;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.OutputStream;
35 import java.net.ServerSocket;
36 import java.net.Socket;
37 import java.net.SocketAddress;
38 import java.net.SocketTimeoutException;
39 import java.net.URI;
40 import java.util.Iterator;
41 import java.util.List;
42
43 import javax.resource.spi.work.Work;
44 import javax.resource.spi.work.WorkException;
45 import javax.resource.spi.work.WorkManager;
46
47 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
48
49
50
51
52
53 public class TcpMessageReceiver extends AbstractMessageReceiver implements Work
54 {
55 private ServerSocket serverSocket = null;
56
57 public TcpMessageReceiver(Connector connector, Service service, InboundEndpoint endpoint)
58 throws CreateException
59 {
60 super(connector, service, endpoint);
61 }
62
63 protected void doConnect() throws ConnectException
64 {
65 disposing.set(false);
66
67 URI uri = endpoint.getEndpointURI().getUri();
68
69 try
70 {
71 serverSocket = ((TcpConnector) connector).getServerSocket(uri);
72 }
73 catch (Exception e)
74 {
75 throw new org.mule.transport.ConnectException(TcpMessages.failedToBindToUri(uri), e, this);
76 }
77
78 try
79 {
80 getWorkManager().scheduleWork(this, WorkManager.INDEFINITE, null, connector);
81 }
82 catch (WorkException e)
83 {
84 throw new ConnectException(CoreMessages.failedToScheduleWork(), e, this);
85 }
86 }
87
88 protected void doDisconnect() throws ConnectException
89 {
90
91 disposing.set(true);
92
93 try
94 {
95 if (serverSocket != null)
96 {
97 if (logger.isDebugEnabled())
98 {
99 logger.debug("Closing: " + serverSocket);
100 }
101 serverSocket.close();
102 }
103 }
104 catch (IOException e)
105 {
106 logger.warn("Failed to close server socket: " + e.getMessage(), e);
107 }
108 }
109
110 protected void doStart() throws MuleException
111 {
112
113 }
114
115 protected void doStop() throws MuleException
116 {
117
118 }
119
120
121
122
123
124 public ServerSocket getServerSocket()
125 {
126 return serverSocket;
127 }
128
129 public void run()
130 {
131 while (!disposing.get())
132 {
133 if (connector.isStarted() && !disposing.get())
134 {
135 Socket socket = null;
136 try
137 {
138 socket = serverSocket.accept();
139
140 if (logger.isTraceEnabled())
141 {
142 logger.trace("Accepted: " + serverSocket);
143 }
144 }
145 catch (java.io.InterruptedIOException iie)
146 {
147 if (logger.isDebugEnabled())
148 {
149 logger.debug("Interupted IO doing serverSocket.accept: " + iie.getMessage());
150 }
151 }
152 catch (Exception e)
153 {
154 if (!connector.isDisposed() && !disposing.get())
155 {
156 logger.warn("Accept failed on socket: " + e, e);
157 handleException(new ConnectException(e, this));
158 }
159 }
160
161 if (socket != null)
162 {
163 try
164 {
165 Work work = createWork(socket);
166 try
167 {
168 getWorkManager().scheduleWork(work, WorkManager.INDEFINITE, null, connector);
169 }
170 catch (WorkException e)
171 {
172 logger.error("Tcp Server receiver Work was not processed: " + e.getMessage(), e);
173 }
174 }
175 catch (IOException e)
176 {
177 handleException(e);
178 }
179 }
180 }
181 }
182 }
183
184 public void release()
185 {
186
187 }
188
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 try
235 {
236 tcpConnector.configureSocket(TcpConnector.SERVER, socket);
237
238 underlyingIn = new BufferedInputStream(socket.getInputStream());
239 dataIn = new TcpInputStream(underlyingIn)
240 {
241 public void close() throws IOException
242 {
243
244
245
246 dataInWorkFinished = true;
247 moreMessages = false;
248
249 synchronized (notify)
250 {
251 notify.notifyAll();
252 }
253 }
254 };
255 dataOut = new BufferedOutputStream(socket.getOutputStream());
256 }
257 catch (IOException e)
258 {
259 logger.error("Failed to set Socket properties: " + e.getMessage(), e);
260 }
261 }
262
263 public void expired()
264 {
265 dispose();
266 }
267
268 public void dispose()
269 {
270 releaseSocket();
271 }
272
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 protected void bindTransaction(Transaction tx) throws TransactionException
349 {
350
351 }
352
353 protected Object getNextMessage(Object resource) throws Exception
354 {
355 long keepAliveTimeout = ((TcpConnector)connector).getKeepAliveTimeout();
356
357 Object readMsg = null;
358 try
359 {
360
361 if(keepAliveTimeout > 0)
362 {
363 ((TcpConnector) connector).getKeepAliveMonitor().addExpirable(keepAliveTimeout,
364 TimeUnit.MILLISECONDS, this);
365 }
366
367 readMsg = protocol.read(dataIn);
368
369
370 ((TcpConnector) connector).getKeepAliveMonitor().removeExpirable(this);
371
372 if (dataIn.isStreaming())
373 {
374 moreMessages = false;
375 }
376
377 return readMsg;
378 }
379 catch (SocketTimeoutException e)
380 {
381 ((TcpConnector) connector).getKeepAliveMonitor().removeExpirable(this);
382 }
383 finally
384 {
385 if (readMsg == null)
386 {
387
388
389
390 dataIn.close();
391 }
392 }
393
394 return null;
395 }
396
397 protected boolean hasMoreMessages(Object message)
398 {
399 return !socket.isClosed() && !dataInWorkFinished
400 && !disposing.get() && moreMessages;
401 }
402
403
404 protected void handleResults(List messages) throws Exception
405 {
406
407 if (endpoint.isRemoteSync() || !service.getOutboundRouter().hasEndpoints())
408 {
409 for (Iterator iterator = messages.iterator(); iterator.hasNext();)
410 {
411 Object o = iterator.next();
412 protocol.write(dataOut, o);
413 dataOut.flush();
414 }
415 }
416 }
417
418 protected void preRouteMuleMessage(final DefaultMuleMessage message) throws Exception
419 {
420 super.preRouteMuleMessage(message);
421
422 final SocketAddress clientAddress = socket.getRemoteSocketAddress();
423 if (clientAddress != null)
424 {
425 message.setProperty(MuleProperties.MULE_REMOTE_CLIENT_ADDRESS, clientAddress.toString());
426 }
427 }
428 }
429
430 }