1
2
3
4
5
6
7
8
9
10
11 package org.mule.providers;
12
13 import org.mule.config.ExceptionHelper;
14 import org.mule.config.MuleProperties;
15 import org.mule.config.i18n.CoreMessages;
16 import org.mule.impl.MuleEvent;
17 import org.mule.impl.MuleMessage;
18 import org.mule.impl.MuleSession;
19 import org.mule.impl.NullSessionHandler;
20 import org.mule.impl.OptimizedRequestContext;
21 import org.mule.impl.RequestContext;
22 import org.mule.impl.ResponseOutputStream;
23 import org.mule.impl.internal.notifications.ConnectionNotification;
24 import org.mule.impl.internal.notifications.MessageNotification;
25 import org.mule.impl.internal.notifications.SecurityNotification;
26 import org.mule.transaction.TransactionCoordination;
27 import org.mule.umo.UMOComponent;
28 import org.mule.umo.UMOEvent;
29 import org.mule.umo.UMOException;
30 import org.mule.umo.UMOMessage;
31 import org.mule.umo.UMOSession;
32 import org.mule.umo.UMOTransaction;
33 import org.mule.umo.endpoint.UMOEndpoint;
34 import org.mule.umo.endpoint.UMOEndpointURI;
35 import org.mule.umo.lifecycle.InitialisationException;
36 import org.mule.umo.manager.UMOWorkManager;
37 import org.mule.umo.provider.UMOConnector;
38 import org.mule.umo.provider.UMOMessageReceiver;
39 import org.mule.umo.security.SecurityException;
40 import org.mule.umo.transformer.TransformerException;
41 import org.mule.umo.transformer.UMOTransformer;
42 import org.mule.util.ClassUtils;
43 import org.mule.util.StringMessageUtils;
44 import org.mule.util.concurrent.WaitableBoolean;
45
46 import java.io.OutputStream;
47
48 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
51
52
53
54
55
56
57 public abstract class AbstractMessageReceiver implements UMOMessageReceiver
58 {
59
60
61
62 protected final Log logger = LogFactory.getLog(getClass());
63
64
65
66
67 protected UMOComponent component = null;
68
69
70
71
72 protected UMOEndpoint endpoint = null;
73
74 private InternalMessageListener listener;
75
76
77
78
79 protected AbstractConnector connector = null;
80
81 protected final AtomicBoolean disposing = new AtomicBoolean(false);
82
83 protected final WaitableBoolean connected = new WaitableBoolean(false);
84
85 protected final WaitableBoolean stopped = new WaitableBoolean(true);
86
87 protected final AtomicBoolean connecting = new AtomicBoolean(false);
88
89
90
91
92
93 protected String receiverKey = null;
94
95
96
97
98
99
100
101 private UMOEndpointURI endpointUri;
102
103 private UMOWorkManager workManager;
104
105 protected ConnectionStrategy connectionStrategy;
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 public AbstractMessageReceiver(UMOConnector connector, UMOComponent component, UMOEndpoint endpoint)
123 throws InitialisationException
124 {
125 setConnector(connector);
126 setComponent(component);
127 setEndpoint(endpoint);
128
129 listener = new DefaultInternalMessageListener();
130 endpointUri = endpoint.getEndpointURI();
131
132 try
133 {
134 workManager = this.connector.getReceiverWorkManager("receiver");
135 }
136 catch (UMOException e)
137 {
138 throw new InitialisationException(e, this);
139 }
140
141 connectionStrategy = this.connector.getConnectionStrategy();
142 }
143
144
145
146
147
148
149 public UMOEndpoint getEndpoint()
150 {
151 return endpoint;
152 }
153
154
155
156
157
158
159 public void handleException(Exception exception)
160 {
161 if (exception instanceof ConnectException)
162 {
163 logger.info("Exception caught is a ConnectException, disconnecting receiver and invoking ReconnectStrategy");
164 try
165 {
166 disconnect();
167 }
168 catch (Exception e)
169 {
170 connector.getExceptionListener().exceptionThrown(e);
171 }
172 }
173 connector.getExceptionListener().exceptionThrown(exception);
174 if (exception instanceof ConnectException)
175 {
176 try
177 {
178 logger.warn("Reconnecting after exception: " + exception.getMessage(), exception);
179 connectionStrategy.connect(this);
180 }
181 catch (UMOException e)
182 {
183 connector.getExceptionListener().exceptionThrown(e);
184 }
185 }
186 }
187
188
189
190
191
192
193
194
195 protected void setExceptionDetails(UMOMessage message, Throwable exception)
196 {
197 String propName = ExceptionHelper.getErrorCodePropertyName(connector.getProtocol());
198
199
200 if (propName != null)
201 {
202 String code = ExceptionHelper.getErrorMapping(connector.getProtocol(), exception.getClass());
203 if (logger.isDebugEnabled())
204 {
205 logger.debug("Setting error code for: " + connector.getProtocol() + ", " + propName + "="
206 + code);
207 }
208 message.setProperty(propName, code);
209 }
210 }
211
212 public UMOConnector getConnector()
213 {
214 return connector;
215 }
216
217 public void setConnector(UMOConnector connector)
218 {
219 if (connector != null)
220 {
221 if (connector instanceof AbstractConnector)
222 {
223 this.connector = (AbstractConnector) connector;
224 }
225 else
226 {
227 throw new IllegalArgumentException(CoreMessages.propertyIsNotSupportedType(
228 "connector", AbstractConnector.class, connector.getClass()).getMessage());
229 }
230 }
231 else
232 {
233 throw new IllegalArgumentException(CoreMessages.objectIsNull("connector").getMessage());
234 }
235 }
236
237 public UMOComponent getComponent()
238 {
239 return component;
240 }
241
242 public final UMOMessage routeMessage(UMOMessage message) throws UMOException
243 {
244 return routeMessage(message, (endpoint.isSynchronous() || TransactionCoordination.getInstance()
245 .getTransaction() != null));
246 }
247
248 public final UMOMessage routeMessage(UMOMessage message, boolean synchronous) throws UMOException
249 {
250 UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
251 return routeMessage(message, tx, tx != null || synchronous, null);
252 }
253
254 public final UMOMessage routeMessage(UMOMessage message, UMOTransaction trans, boolean synchronous)
255 throws UMOException
256 {
257 return routeMessage(message, trans, synchronous, null);
258 }
259
260 public final UMOMessage routeMessage(UMOMessage message, OutputStream outputStream) throws UMOException
261 {
262 return routeMessage(message, endpoint.isSynchronous(), outputStream);
263 }
264
265 public final UMOMessage routeMessage(UMOMessage message, boolean synchronous, OutputStream outputStream)
266 throws UMOException
267 {
268 UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
269 return routeMessage(message, tx, tx != null || synchronous, outputStream);
270 }
271
272 public final UMOMessage routeMessage(UMOMessage message,
273 UMOTransaction trans,
274 boolean synchronous,
275 OutputStream outputStream) throws UMOException
276 {
277
278 if (connector.isEnableMessageEvents())
279 {
280 connector.fireNotification(new MessageNotification(message, endpoint, component.getDescriptor()
281 .getName(), MessageNotification.MESSAGE_RECEIVED));
282 }
283
284 if (endpoint.isRemoteSync())
285 {
286 message.setBooleanProperty(MuleProperties.MULE_REMOTE_SYNC_PROPERTY, true);
287 }
288
289 if (logger.isDebugEnabled())
290 {
291 logger.debug("Message Received from: " + endpoint.getEndpointURI());
292 }
293 if (logger.isTraceEnabled())
294 {
295 try
296 {
297 logger.trace("Message Payload: \n"
298 + StringMessageUtils.truncate(StringMessageUtils.toString(message.getPayload()),
299 200, false));
300 }
301 catch (Exception e)
302 {
303
304 }
305 }
306
307
308 if (endpoint.getFilter() != null)
309 {
310 if (!endpoint.getFilter().accept(message))
311 {
312
313
314
315
316 message = handleUnacceptedFilter(message);
317 RequestContext.setEvent(new MuleEvent(message, endpoint,
318 new MuleSession(message, new NullSessionHandler()), synchronous));
319 return message;
320 }
321 }
322 return listener.onMessage(message, trans, synchronous, outputStream);
323 }
324
325 protected UMOMessage handleUnacceptedFilter(UMOMessage message)
326 {
327 String messageId;
328 messageId = message.getUniqueId();
329
330 if (logger.isDebugEnabled())
331 {
332 logger.debug("Message " + messageId + " failed to pass filter on endpoint: " + endpoint
333 + ". Message is being ignored");
334 }
335
336 return null;
337 }
338
339
340
341
342
343
344 public void setEndpoint(UMOEndpoint endpoint)
345 {
346 if (endpoint == null)
347 {
348 throw new IllegalArgumentException("Endpoint cannot be null");
349 }
350 this.endpoint = endpoint;
351 }
352
353
354
355
356
357
358 public void setComponent(UMOComponent component)
359 {
360 if (component == null)
361 {
362 throw new IllegalArgumentException("Component cannot be null");
363 }
364 this.component = component;
365 }
366
367 public final void dispose()
368 {
369 stop();
370 disposing.set(true);
371 doDispose();
372 }
373
374 public UMOEndpointURI getEndpointURI()
375 {
376 return endpointUri;
377 }
378
379 protected UMOWorkManager getWorkManager()
380 {
381 return workManager;
382 }
383
384 protected void setWorkManager(UMOWorkManager workManager)
385 {
386 this.workManager = workManager;
387 }
388
389 public void connect() throws Exception
390 {
391 if (connected.get())
392 {
393 return;
394 }
395
396 if (connecting.compareAndSet(false, true))
397 {
398 if (logger.isDebugEnabled())
399 {
400 logger.debug("Connecting: " + this);
401 }
402
403 connectionStrategy.connect(this);
404
405 logger.info("Connected: " + this);
406 return;
407 }
408
409 try
410 {
411 this.doConnect();
412 connected.set(true);
413 connecting.set(false);
414
415 connector.fireNotification(new ConnectionNotification(this, getConnectEventId(),
416 ConnectionNotification.CONNECTION_CONNECTED));
417 }
418 catch (Exception e)
419 {
420 connected.set(false);
421 connecting.set(false);
422
423 connector.fireNotification(new ConnectionNotification(this, getConnectEventId(),
424 ConnectionNotification.CONNECTION_FAILED));
425
426 if (e instanceof ConnectException)
427 {
428 throw (ConnectException) e;
429 }
430 else
431 {
432 throw new ConnectException(e, this);
433 }
434 }
435 }
436
437 public void disconnect() throws Exception
438 {
439 if (logger.isDebugEnabled())
440 {
441 logger.debug("Disconnecting: " + this);
442 }
443
444 this.doDisconnect();
445 connected.set(false);
446
447 logger.info("Disconnected: " + this);
448
449 connector.fireNotification(new ConnectionNotification(this, getConnectEventId(),
450 ConnectionNotification.CONNECTION_DISCONNECTED));
451 }
452
453 public String getConnectionDescription()
454 {
455 return endpoint.getEndpointURI().toString();
456 }
457
458 public final void start() throws UMOException
459 {
460 if (stopped.compareAndSet(true, false))
461 {
462 if (!connected.get())
463 {
464 connectionStrategy.connect(this);
465 }
466 doStart();
467 }
468 }
469
470 public final void stop()
471 {
472 try
473 {
474 if (connected.get())
475 {
476 disconnect();
477 }
478 }
479 catch (Exception e)
480 {
481
482 logger.error(e.getMessage(), e);
483 }
484
485 if (stopped.compareAndSet(false, true))
486 {
487 try
488 {
489 doStop();
490 }
491 catch (UMOException e)
492 {
493
494 logger.error(e.getMessage(), e);
495 }
496
497 }
498 }
499
500 public final boolean isConnected()
501 {
502 return connected.get();
503 }
504
505 public InternalMessageListener getListener()
506 {
507 return listener;
508 }
509
510 public void setListener(InternalMessageListener listener)
511 {
512 this.listener = listener;
513 }
514
515 private class DefaultInternalMessageListener implements InternalMessageListener
516 {
517
518 public UMOMessage onMessage(UMOMessage message,
519 UMOTransaction trans,
520 boolean synchronous,
521 OutputStream outputStream) throws UMOException
522 {
523
524 UMOMessage resultMessage = null;
525 ResponseOutputStream ros = null;
526 if (outputStream != null)
527 {
528 if (outputStream instanceof ResponseOutputStream)
529 {
530 ros = (ResponseOutputStream) outputStream;
531 }
532 else
533 {
534 ros = new ResponseOutputStream(outputStream);
535 }
536 }
537 UMOSession session = new MuleSession(message, connector.getSessionHandler(), component);
538 UMOEvent muleEvent = new MuleEvent(message, endpoint, session, synchronous, ros);
539 muleEvent = OptimizedRequestContext.unsafeSetEvent(muleEvent);
540 message = muleEvent.getMessage();
541
542
543 boolean authorised = false;
544 if (endpoint.getSecurityFilter() != null)
545 {
546 try
547 {
548 endpoint.getSecurityFilter().authenticate(muleEvent);
549 authorised = true;
550 }
551 catch (SecurityException e)
552 {
553
554 logger.warn("Request was made but was not authenticated: " + e.getMessage(), e);
555 connector.fireNotification(new SecurityNotification(e,
556 SecurityNotification.SECURITY_AUTHENTICATION_FAILED));
557 handleException(e);
558 resultMessage = message;
559
560 }
561 }
562 else
563 {
564 authorised = true;
565 }
566
567 if (authorised)
568 {
569
570
571 muleEvent = RequestContext.getEvent();
572
573
574 if (UMOEndpoint.ENDPOINT_TYPE_RESPONSE.equals(endpoint.getType()))
575 {
576 component.getDescriptor().getResponseRouter().route(muleEvent);
577 return null;
578 }
579 else
580 {
581 resultMessage = component.getDescriptor().getInboundRouter().route(muleEvent);
582 }
583 }
584 if (resultMessage != null)
585 {
586 if (resultMessage.getExceptionPayload() != null)
587 {
588 setExceptionDetails(resultMessage, resultMessage.getExceptionPayload().getException());
589 }
590 OptimizedRequestContext.unsafeRewriteEvent(resultMessage);
591 }
592 return applyResponseTransformer(resultMessage);
593 }
594 }
595
596 protected String getConnectEventId()
597 {
598 return connector.getName() + ".receiver (" + endpoint.getEndpointURI() + ")";
599 }
600
601 protected UMOMessage applyResponseTransformer(UMOMessage returnMessage) throws TransformerException
602 {
603 UMOTransformer transformer = endpoint.getResponseTransformer();
604
605
606 if (transformer == null)
607 {
608 transformer = component.getDescriptor().getResponseTransformer();
609 }
610
611
612 if (transformer == null)
613 {
614 return returnMessage;
615 }
616
617 if (returnMessage == null)
618 {
619 if (transformer.isAcceptNull())
620 {
621 returnMessage = new MuleMessage(NullPayload.getInstance(), RequestContext.getEventContext()
622 .getMessage());
623 }
624 else
625 {
626 return null;
627 }
628 }
629
630 Object returnPayload = returnMessage.getPayload();
631 if (transformer.isSourceTypeSupported(returnPayload.getClass()))
632 {
633 Object result = transformer.transform(returnPayload);
634 if (result instanceof UMOMessage)
635 {
636 returnMessage = (UMOMessage) result;
637 }
638 else
639 {
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656 returnMessage = new MuleMessage(result, RequestContext.getEvent().getMessage());
657
658
659 }
660 }
661 else
662 {
663 if (logger.isDebugEnabled())
664 {
665 logger.debug("Response transformer: " + transformer + " doesn't support the result payload: "
666 + returnPayload.getClass());
667 }
668 }
669 return returnMessage;
670 }
671
672 public void setReceiverKey(String receiverKey)
673 {
674 this.receiverKey = receiverKey;
675 }
676
677 public String getReceiverKey()
678 {
679 return receiverKey;
680 }
681
682 public String toString()
683 {
684 final StringBuffer sb = new StringBuffer(80);
685 sb.append(ClassUtils.getSimpleName(this.getClass()));
686 sb.append("{this=").append(Integer.toHexString(System.identityHashCode(this)));
687 sb.append(", receiverKey=").append(receiverKey);
688 sb.append(", endpoint=").append(endpoint.getEndpointURI());
689 sb.append('}');
690 return sb.toString();
691 }
692
693 protected abstract void doStart() throws UMOException;
694
695 protected abstract void doStop() throws UMOException;
696
697 protected abstract void doConnect() throws Exception;
698
699 protected abstract void doDisconnect() throws Exception;
700
701 protected abstract void doDispose();
702
703 }