001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.jbi.messaging;
018
019 import java.util.ArrayList;
020 import java.util.List;
021 import java.util.Map;
022 import java.util.concurrent.ArrayBlockingQueue;
023 import java.util.concurrent.BlockingQueue;
024 import java.util.concurrent.ConcurrentHashMap;
025 import java.util.concurrent.TimeUnit;
026 import java.util.concurrent.atomic.AtomicBoolean;
027
028 import javax.jbi.JBIException;
029 import javax.jbi.component.Component;
030 import javax.jbi.component.ComponentLifeCycle;
031 import javax.jbi.messaging.DeliveryChannel;
032 import javax.jbi.messaging.ExchangeStatus;
033 import javax.jbi.messaging.MessageExchange;
034 import javax.jbi.messaging.MessageExchange.Role;
035 import javax.jbi.messaging.MessageExchangeFactory;
036 import javax.jbi.messaging.MessagingException;
037 import javax.jbi.servicedesc.ServiceEndpoint;
038 import javax.transaction.Transaction;
039 import javax.transaction.TransactionManager;
040 import javax.xml.namespace.QName;
041
042 import org.apache.commons.logging.Log;
043 import org.apache.commons.logging.LogFactory;
044 import org.apache.servicemix.JbiConstants;
045 import org.apache.servicemix.MessageExchangeListener;
046 import org.apache.servicemix.id.IdGenerator;
047 import org.apache.servicemix.jbi.ExchangeTimeoutException;
048 import org.apache.servicemix.jbi.container.ActivationSpec;
049 import org.apache.servicemix.jbi.container.JBIContainer;
050 import org.apache.servicemix.jbi.event.ExchangeEvent;
051 import org.apache.servicemix.jbi.event.ExchangeListener;
052 import org.apache.servicemix.jbi.framework.ComponentContextImpl;
053 import org.apache.servicemix.jbi.framework.ComponentMBeanImpl;
054
055 /**
056 * DeliveryChannel implementation
057 *
058 * @version $Revision: 695373 $
059 */
060 public class DeliveryChannelImpl implements DeliveryChannel {
061
062 private static final Log LOG = LogFactory.getLog(DeliveryChannelImpl.class);
063
064 private JBIContainer container;
065
066 private ComponentContextImpl context;
067
068 private ComponentMBeanImpl component;
069
070 private BlockingQueue<MessageExchangeImpl> queue;
071
072 private IdGenerator idGenerator = new IdGenerator();
073
074 private MessageExchangeFactory inboundFactory;
075
076 private int intervalCount;
077
078 private AtomicBoolean closed = new AtomicBoolean(false);
079
080 private Map<Thread, Boolean> waiters = new ConcurrentHashMap<Thread, Boolean>();
081
082 private TransactionManager transactionManager;
083
084 /**
085 * When using clustering and sendSync, the exchange received will not be the
086 * same as the one sent (because it has been serialized/deserialized. We
087 * thus need to keep the original exchange in a map and override its state.
088 */
089 private Map<String, MessageExchangeImpl> exchangesById = new ConcurrentHashMap<String, MessageExchangeImpl>();
090
091 /**
092 * Constructor
093 */
094 public DeliveryChannelImpl(ComponentMBeanImpl component) {
095 this.component = component;
096 this.container = component.getContainer();
097 this.queue = new ArrayBlockingQueue<MessageExchangeImpl>(component.getInboundQueueCapacity());
098 this.transactionManager = (TransactionManager) this.container.getTransactionManager();
099 }
100
101 /**
102 * @return size of the inbound Queue
103 */
104 public int getQueueSize() {
105 return queue.size();
106 }
107
108 /**
109 * close the delivery channel
110 *
111 * @throws MessagingException
112 */
113 public void close() throws MessagingException {
114 if (this.closed.compareAndSet(false, true)) {
115 if (LOG.isDebugEnabled()) {
116 LOG.debug("Closing DeliveryChannel " + this);
117 }
118 List<MessageExchangeImpl> pending = new ArrayList<MessageExchangeImpl>(queue.size());
119 queue.drainTo(pending);
120 for (MessageExchangeImpl messageExchange : pending) {
121 if (messageExchange.getTransactionContext() != null
122 && messageExchange.getMirror().getSyncState() == MessageExchangeImpl.SYNC_STATE_SYNC_SENT) {
123 notifyExchange(messageExchange.getMirror(), messageExchange.getMirror(), "close");
124 }
125 }
126 // Interrupt all blocked thread
127 Thread[] threads = waiters.keySet().toArray(new Thread[waiters.size()]);
128 for (int i = 0; i < threads.length; i++) {
129 threads[i].interrupt();
130 }
131 // deactivate all endpoints from this component
132 ServiceEndpoint[] endpoints = container.getRegistry().getEndpointsForComponent(component.getComponentNameSpace());
133 for (int i = 0; i < endpoints.length; i++) {
134 try {
135 component.getContext().deactivateEndpoint(endpoints[i]);
136 } catch (JBIException e) {
137 LOG.error("Error deactivating endpoint", e);
138 }
139 }
140 // TODO: Cause all accepts to return null
141 // TODO: Abort all pending exchanges
142 }
143 }
144
145 protected void checkNotClosed() throws MessagingException {
146 if (closed.get()) {
147 throw new MessagingException(this + " has been closed.");
148 }
149 }
150
151 /**
152 * Create a message exchange factory. This factory will create exchange
153 * instances with all appropriate properties set to null.
154 *
155 * @return a message exchange factory
156 */
157 public MessageExchangeFactory createExchangeFactory() {
158 MessageExchangeFactoryImpl result = createMessageExchangeFactory();
159 result.setContext(context);
160 ActivationSpec activationSpec = context.getActivationSpec();
161 if (activationSpec != null) {
162 String componentName = context.getComponentNameSpace().getName();
163 // lets auto-default the container-routing information
164 QName serviceName = activationSpec.getDestinationService();
165 if (serviceName != null) {
166 result.setServiceName(serviceName);
167 LOG.debug("default destination serviceName for " + componentName + " = " + serviceName);
168 }
169 QName interfaceName = activationSpec.getDestinationInterface();
170 if (interfaceName != null) {
171 result.setInterfaceName(interfaceName);
172 LOG.debug("default destination interfaceName for " + componentName + " = " + interfaceName);
173 }
174 QName operationName = activationSpec.getDestinationOperation();
175 if (operationName != null) {
176 result.setOperationName(operationName);
177 LOG.debug("default destination operationName for " + componentName + " = " + operationName);
178 }
179 String endpointName = activationSpec.getDestinationEndpoint();
180 if (endpointName != null) {
181 boolean endpointSet = false;
182 LOG.debug("default destination endpointName for " + componentName + " = " + endpointName);
183 if (serviceName != null && endpointName != null) {
184 endpointName = endpointName.trim();
185 ServiceEndpoint endpoint = container.getRegistry().getEndpoint(serviceName, endpointName);
186 if (endpoint != null) {
187 result.setEndpoint(endpoint);
188 LOG.info("Set default destination endpoint for " + componentName + " to " + endpoint);
189 endpointSet = true;
190 }
191 }
192 if (!endpointSet) {
193 LOG.warn("Could not find destination endpoint for " + componentName + " service(" + serviceName
194 + ") with endpointName " + endpointName);
195 }
196 }
197 }
198 return result;
199 }
200
201 /**
202 * Create a message exchange factory for the given interface name.
203 *
204 * @param interfaceName
205 * name of the interface for which all exchanges created by the
206 * returned factory will be set
207 * @return an exchange factory that will create exchanges for the given
208 * interface; must be non-null
209 */
210 public MessageExchangeFactory createExchangeFactory(QName interfaceName) {
211 MessageExchangeFactoryImpl result = createMessageExchangeFactory();
212 result.setInterfaceName(interfaceName);
213 return result;
214 }
215
216 /**
217 * Create a message exchange factory for the given service name.
218 *
219 * @param serviceName
220 * name of the service for which all exchanges created by the
221 * returned factory will be set
222 * @return an exchange factory that will create exchanges for the given
223 * service; must be non-null
224 */
225 public MessageExchangeFactory createExchangeFactoryForService(QName serviceName) {
226 MessageExchangeFactoryImpl result = createMessageExchangeFactory();
227 result.setServiceName(serviceName);
228 return result;
229 }
230
231 /**
232 * Create a message exchange factory for the given endpoint.
233 *
234 * @param endpoint
235 * endpoint for which all exchanges created by the returned
236 * factory will be set for
237 * @return an exchange factory that will create exchanges for the given
238 * endpoint
239 */
240 public MessageExchangeFactory createExchangeFactory(ServiceEndpoint endpoint) {
241 MessageExchangeFactoryImpl result = createMessageExchangeFactory();
242 result.setEndpoint(endpoint);
243 return result;
244 }
245
246 protected MessageExchangeFactoryImpl createMessageExchangeFactory() {
247 MessageExchangeFactoryImpl messageExchangeFactory = new MessageExchangeFactoryImpl(idGenerator, closed);
248 messageExchangeFactory.setContext(context);
249 return messageExchangeFactory;
250 }
251
252 /**
253 * @return a MessageExchange - blocking call
254 * @throws MessagingException
255 */
256 public MessageExchange accept() throws MessagingException {
257 return accept(Long.MAX_VALUE);
258 }
259
260 /**
261 * return a MessageExchange
262 *
263 * @param timeoutMS
264 * @return Message Exchange
265 * @throws MessagingException
266 */
267 public MessageExchange accept(long timeoutMS) throws MessagingException {
268 try {
269 checkNotClosed();
270 MessageExchangeImpl me = queue.poll(timeoutMS, TimeUnit.MILLISECONDS);
271 if (me != null) {
272 // If the exchange has already timed out,
273 // do not give it to the component
274 if (me.getPacket().isAborted()) {
275 if (LOG.isDebugEnabled()) {
276 LOG.debug("Aborted " + me.getExchangeId() + " in " + this);
277 }
278 me = null;
279 } else {
280 if (LOG.isDebugEnabled()) {
281 LOG.debug("Accepting " + me.getExchangeId() + " in " + this);
282 }
283 // If we have a tx lock and the exchange is not active, we
284 // need
285 // to notify here without resuming transaction
286 if (me.getTxLock() != null && me.getStatus() != ExchangeStatus.ACTIVE) {
287 notifyExchange(me.getMirror(), me.getTxLock(), "acceptFinishedExchangeWithTxLock");
288 me.handleAccept();
289 if (LOG.isTraceEnabled()) {
290 LOG.trace("Accepted: " + me);
291 }
292 // We transactionnaly deliver a finished exchange
293 } else if (me.isTransacted() && me.getStatus() != ExchangeStatus.ACTIVE) {
294 // Do not resume transaction
295 me.handleAccept();
296 if (LOG.isTraceEnabled()) {
297 LOG.trace("Accepted: " + me);
298 }
299 } else {
300 resumeTx(me);
301 me.handleAccept();
302 if (LOG.isTraceEnabled()) {
303 LOG.trace("Accepted: " + me);
304 }
305 }
306 }
307 }
308 if (me != null) {
309 // Call input listeners
310 ExchangeListener[] l = (ExchangeListener[]) container.getListeners(ExchangeListener.class);
311 ExchangeEvent event = new ExchangeEvent(me, ExchangeEvent.EXCHANGE_ACCEPTED);
312 for (int i = 0; i < l.length; i++) {
313 try {
314 l[i].exchangeAccepted(event);
315 } catch (Exception e) {
316 LOG.warn("Error calling listener: " + e.getMessage(), e);
317 }
318 }
319 }
320 return me;
321 } catch (InterruptedException e) {
322 throw new MessagingException("accept failed", e);
323 }
324 }
325
326 protected void autoSetPersistent(MessageExchangeImpl me) {
327 Boolean persistent = me.getPersistent();
328 if (persistent == null) {
329 if (context.getActivationSpec().getPersistent() != null) {
330 persistent = context.getActivationSpec().getPersistent();
331 } else {
332 persistent = Boolean.valueOf(context.getContainer().isPersistent());
333 }
334 me.setPersistent(persistent);
335 }
336 }
337
338 protected void throttle() {
339 if (component.isExchangeThrottling()) {
340 if (component.getThrottlingInterval() > intervalCount) {
341 intervalCount = 0;
342 try {
343 Thread.sleep(component.getThrottlingTimeout());
344 } catch (InterruptedException e) {
345 LOG.warn("throttling failed", e);
346 }
347 }
348 intervalCount++;
349 }
350 }
351
352 protected void doSend(MessageExchangeImpl me, boolean sync) throws MessagingException {
353 MessageExchangeImpl mirror = me.getMirror();
354 boolean finished = me.getStatus() != ExchangeStatus.ACTIVE;
355 try {
356 if (LOG.isTraceEnabled()) {
357 LOG.trace("Sent: " + me);
358 }
359 // If the message has timed out
360 if (me.getPacket().isAborted()) {
361 throw new ExchangeTimeoutException(me);
362 }
363 // Auto enlist exchange in transaction
364 autoEnlistInTx(me);
365 // Update persistence info
366 autoSetPersistent(me);
367 // Throttle if needed
368 throttle();
369 // Store the consumer component
370 if (me.getRole() == Role.CONSUMER) {
371 me.setSourceId(component.getComponentNameSpace());
372 }
373 // Call the listeners before the ownership changes
374 // Call input listeners
375 ExchangeListener[] l = (ExchangeListener[]) container.getListeners(ExchangeListener.class);
376 ExchangeEvent event = new ExchangeEvent(me, ExchangeEvent.EXCHANGE_SENT);
377 for (int i = 0; i < l.length; i++) {
378 try {
379 l[i].exchangeSent(event);
380 } catch (Exception e) {
381 LOG.warn("Error calling listener: " + e.getMessage(), e);
382 }
383 }
384 // Change ownership
385 me.handleSend(sync);
386 mirror.setTxState(MessageExchangeImpl.TX_STATE_NONE);
387 // If this is the DONE or ERROR status from a synchronous
388 // transactional exchange,
389 // it should not be part of the transaction, so remove the tx
390 // context
391 if (finished && me.getTxLock() == null && me.getTxState() == MessageExchangeImpl.TX_STATE_CONVEYED
392 && !me.isPushDelivery() && me.getRole() == Role.CONSUMER) {
393 me.setTransactionContext(null);
394 }
395 container.sendExchange(mirror);
396 } catch (MessagingException e) {
397 if (LOG.isDebugEnabled()) {
398 LOG.debug("Exception processing: " + me.getExchangeId() + " in " + this);
399 }
400 throw e;
401 } finally {
402 // If there is a tx lock, we need to suspend and notify
403 if (me.getTxLock() != null) {
404 if (mirror.getTxState() == MessageExchangeImpl.TX_STATE_ENLISTED) {
405 suspendTx(mirror);
406 }
407 synchronized (me.getTxLock()) {
408 notifyExchange(me, me.getTxLock(), "doSendWithTxLock");
409 }
410 }
411 }
412 }
413
414 /**
415 * routes a MessageExchange
416 *
417 * @param messageExchange
418 * @throws MessagingException
419 */
420 public void send(MessageExchange messageExchange) throws MessagingException {
421 // If the delivery channel has been closed
422 checkNotClosed();
423 // Log call
424 if (LOG.isDebugEnabled()) {
425 LOG.debug("Send " + messageExchange.getExchangeId() + " in " + this);
426 }
427 // // JBI 5.5.2.1.3: remove sync property
428 messageExchange.setProperty(JbiConstants.SEND_SYNC, null);
429 // Call doSend
430 MessageExchangeImpl me = (MessageExchangeImpl) messageExchange;
431 doSend(me, false);
432 }
433
434 /**
435 * routes a MessageExchange
436 *
437 * @param messageExchange
438 * @return true if processed
439 * @throws MessagingException
440 */
441 public boolean sendSync(MessageExchange messageExchange) throws MessagingException {
442 return sendSync(messageExchange, 0);
443 }
444
445 /**
446 * routes a MessageExchange
447 *
448 * @param messageExchange
449 * @param timeout
450 * @return true if processed
451 * @throws MessagingException
452 */
453 public boolean sendSync(MessageExchange messageExchange, long timeout) throws MessagingException {
454 // If the delivery channel has been closed
455 checkNotClosed();
456 // Log call
457 if (LOG.isDebugEnabled()) {
458 LOG.debug("SendSync " + messageExchange.getExchangeId() + " in " + this);
459 }
460 boolean result = false;
461 // JBI 5.5.2.1.3: set the sendSync property
462 messageExchange.setProperty(JbiConstants.SEND_SYNC, Boolean.TRUE);
463 // Call doSend
464 MessageExchangeImpl me = (MessageExchangeImpl) messageExchange;
465 String exchangeKey = me.getKey();
466 try {
467 exchangesById.put(exchangeKey, me);
468 // Synchronously send a message and wait for the response
469 synchronized (me) {
470 doSend(me, true);
471 if (me.getSyncState() != MessageExchangeImpl.SYNC_STATE_SYNC_RECEIVED) {
472 waitForExchange(me, me, timeout, "sendSync");
473 } else {
474 if (LOG.isDebugEnabled()) {
475 LOG.debug("Exchange " + messageExchange.getExchangeId() + " has already been answered (no need to wait)");
476 }
477 }
478 }
479 if (me.getSyncState() == MessageExchangeImpl.SYNC_STATE_SYNC_RECEIVED) {
480 me.handleAccept();
481 // If the sender flag has been removed, it means
482 // the message has been delivered in the same thread
483 // so there is no need to resume the transaction
484 // See processInBound
485 // if (messageExchangeImpl.getSyncSenderThread() != null) {
486 resumeTx(me);
487 // }
488 // Call input listeners
489 ExchangeListener[] l = (ExchangeListener[]) container.getListeners(ExchangeListener.class);
490 ExchangeEvent event = new ExchangeEvent(me, ExchangeEvent.EXCHANGE_ACCEPTED);
491 for (int i = 0; i < l.length; i++) {
492 try {
493 l[i].exchangeSent(event);
494 } catch (Exception e) {
495 LOG.warn("Error calling listener: " + e.getMessage(), e);
496 }
497 }
498 result = true;
499 } else {
500 // JBI 5.5.2.1.3: the exchange should be set to ERROR status
501 if (LOG.isDebugEnabled()) {
502 LOG.debug("Exchange " + messageExchange.getExchangeId() + " has been aborted");
503 }
504 me.getPacket().setAborted(true);
505 result = false;
506 }
507 } catch (InterruptedException e) {
508 throw new MessagingException(e);
509 } catch (RuntimeException e) {
510 // e.printStackTrace();
511 throw e;
512 } finally {
513 exchangesById.remove(exchangeKey);
514 }
515 return result;
516 }
517
518 /**
519 * @return Returns the container.
520 */
521 public JBIContainer getContainer() {
522 return container;
523 }
524
525 /**
526 * @param container
527 * The container to set.
528 */
529 public void setContainer(JBIContainer container) {
530 this.container = container;
531 }
532
533 /**
534 * @return Returns the componentConnector.
535 */
536 public ComponentMBeanImpl getComponent() {
537 return component;
538 }
539
540 /**
541 * Get the context
542 *
543 * @return the context
544 */
545 public ComponentContextImpl getContext() {
546 return context;
547 }
548
549 /**
550 * set the context
551 *
552 * @param context
553 */
554 public void setContext(ComponentContextImpl context) {
555 this.context = context;
556 }
557
558 /**
559 * Used internally for passing in a MessageExchange
560 *
561 * @param me
562 * @throws MessagingException
563 */
564 public void processInBound(MessageExchangeImpl me) throws MessagingException {
565 if (LOG.isTraceEnabled()) {
566 LOG.trace("Processing inbound exchange: " + me);
567 }
568 // Check if the delivery channel has been closed
569 checkNotClosed();
570 // Retrieve the original exchange sent
571 MessageExchangeImpl original = exchangesById.get(me.getKey());
572 if (original != null && me != original) {
573 original.copyFrom(me);
574 me = original;
575 }
576 // Check if the incoming exchange is a response to a synchronous
577 // exchange previously sent
578 // In this case, we do not have to queue it, but rather notify the
579 // waiting thread.
580 if (me.getSyncState() == MessageExchangeImpl.SYNC_STATE_SYNC_SENT) {
581 // If the mirror has been delivered using push, better wait until
582 // the push call return. This can only work if not using clustered
583 // flows,
584 // but the flag is transient so we do not care.
585 // Ensure that data is uptodate with the incoming exchange (in
586 // case the exchange has
587 // been serialized / deserialized by a clustered flow)
588 suspendTx(original);
589 me.setSyncState(MessageExchangeImpl.SYNC_STATE_SYNC_RECEIVED);
590 notifyExchange(original, original, "processInboundSynchronousExchange");
591 return;
592 }
593
594 // If the component implements the MessageExchangeListener,
595 // the delivery can be made synchronously, so we don't need
596 // to bother with transactions
597 MessageExchangeListener listener = getExchangeListener();
598 if (listener != null) {
599 me.handleAccept();
600 if (LOG.isTraceEnabled()) {
601 LOG.trace("Received: " + me);
602 }
603 // Call input listeners
604 ExchangeListener[] l = (ExchangeListener[]) container.getListeners(ExchangeListener.class);
605 ExchangeEvent event = new ExchangeEvent(me, ExchangeEvent.EXCHANGE_ACCEPTED);
606 for (int i = 0; i < l.length; i++) {
607 try {
608 l[i].exchangeAccepted(event);
609 } catch (Exception e) {
610 LOG.warn("Error calling listener: " + e.getMessage(), e);
611 }
612 }
613 // Set the flag the the exchange was delivered using push mode
614 // This is important for transaction boundaries
615 me.setPushDeliver(true);
616 // Deliver the exchange
617 ClassLoader old = Thread.currentThread().getContextClassLoader();
618 try {
619 Thread.currentThread().setContextClassLoader(component.getComponent().getClass().getClassLoader());
620 listener.onMessageExchange(me);
621 } finally {
622 Thread.currentThread().setContextClassLoader(old);
623 }
624 // TODO: handle delayed exchange notifications
625 return;
626 }
627
628 // Component uses pull delivery.
629
630 // If the exchange is transacted, special care should be taken.
631 // But if the exchange is no more ACTIVE, just queue it, as
632 // we will never have an answer back.
633 if (me.isTransacted() && me.getStatus() == ExchangeStatus.ACTIVE) {
634 // If the transaction is conveyed by the exchange
635 // We do not need to resume the transaction in this thread
636 if (me.getTxState() == MessageExchangeImpl.TX_STATE_CONVEYED) {
637 try {
638 suspendTx(me);
639 queue.put(me);
640 } catch (InterruptedException e) {
641 LOG.debug("Exchange " + me.getExchangeId() + " aborted due to thread interruption", e);
642 me.getPacket().setAborted(true);
643 }
644 // Else the delivery / send are enlisted in the current tx.
645 // We must suspend the transaction, queue it, and wait for the
646 // answer
647 // to be sent, at which time the tx should be suspended and resumed
648 // in
649 // this thread.
650 } else {
651 Object lock = new Object();
652 synchronized (lock) {
653 try {
654 me.setTxLock(lock);
655 suspendTx(me);
656 queue.put(me);
657 waitForExchange(me, lock, 0, "processInboundTransactionalExchange");
658 } catch (InterruptedException e) {
659 LOG.debug("Exchange " + me.getExchangeId() + " aborted due to thread interruption", e);
660 me.getPacket().setAborted(true);
661 } finally {
662 me.setTxLock(null);
663 resumeTx(me);
664 }
665 }
666 }
667 // If the exchange is ACTIVE, the transaction boundary will suspended
668 // when the
669 // answer is sent
670 // Else just queue the exchange
671 } else {
672 try {
673 queue.put(me);
674 } catch (InterruptedException e) {
675 LOG.debug("Exchange " + me.getExchangeId() + " aborted due to thread interruption", e);
676 me.getPacket().setAborted(true);
677 }
678 }
679 }
680
681 protected MessageExchangeListener getExchangeListener() {
682 Component comp = this.component.getComponent();
683 if (comp instanceof MessageExchangeListener) {
684 return (MessageExchangeListener) comp;
685 }
686 ComponentLifeCycle lifecycle = this.component.getLifeCycle();
687 if (lifecycle instanceof MessageExchangeListener) {
688 return (MessageExchangeListener) lifecycle;
689 }
690 return null;
691 }
692
693 /**
694 * Synchronization must be performed on the given exchange when calling this
695 * method
696 *
697 * @param me
698 * @throws InterruptedException
699 */
700 protected void waitForExchange(MessageExchangeImpl me, Object lock, long timeout, String from) throws InterruptedException {
701 // If the channel is closed while here, we must abort
702 if (LOG.isDebugEnabled()) {
703 LOG.debug("Waiting for exchange " + me.getExchangeId() + " (" + Integer.toHexString(me.hashCode()) + ") to be answered in "
704 + this + " from " + from);
705 }
706 Thread th = Thread.currentThread();
707 try {
708 waiters.put(th, Boolean.TRUE);
709 lock.wait(timeout);
710 } finally {
711 waiters.remove(th);
712 }
713 if (LOG.isDebugEnabled()) {
714 LOG.debug("Notified: " + me.getExchangeId() + "(" + Integer.toHexString(me.hashCode()) + ") in " + this + " from " + from);
715 }
716 }
717
718 protected void notifyExchange(MessageExchangeImpl me, Object lock, String from) {
719 if (LOG.isDebugEnabled()) {
720 LOG.debug("Notifying exchange " + me.getExchangeId() + "(" + Integer.toHexString(me.hashCode()) + ") in " + this + " from "
721 + from);
722 }
723 synchronized (lock) {
724 lock.notify();
725 }
726 }
727
728 /**
729 * Get Inbound Factory
730 *
731 * @return the inbound message factory
732 */
733 public MessageExchangeFactory getInboundFactory() {
734 if (inboundFactory == null) {
735 inboundFactory = createExchangeFactory();
736 }
737 return inboundFactory;
738 }
739
740 protected void suspendTx(MessageExchangeImpl me) {
741 if (transactionManager != null && !container.isUseNewTransactionModel()) {
742 try {
743 Transaction oldTx = me.getTransactionContext();
744 if (oldTx != null) {
745 if (LOG.isDebugEnabled()) {
746 LOG.debug("Suspending transaction for " + me.getExchangeId() + " in " + this);
747 }
748 Transaction tx = transactionManager.suspend();
749 if (tx != oldTx) {
750 throw new IllegalStateException(
751 "the transaction context set in the messageExchange is not bound to the current thread");
752 }
753 }
754 } catch (Exception e) {
755 LOG.info("Exchange " + me.getExchangeId() + " aborted due to transaction exception", e);
756 me.getPacket().setAborted(true);
757 }
758 }
759 }
760
761 protected void resumeTx(MessageExchangeImpl me) throws MessagingException {
762 if (transactionManager != null && !container.isUseNewTransactionModel()) {
763 try {
764 Transaction oldTx = me.getTransactionContext();
765 if (oldTx != null) {
766 if (LOG.isDebugEnabled()) {
767 LOG.debug("Resuming transaction for " + me.getExchangeId() + " in " + this);
768 }
769 transactionManager.resume(oldTx);
770 }
771 } catch (Exception e) {
772 throw new MessagingException(e);
773 }
774 }
775 }
776
777 /**
778 * If the jbi container configured to do so, the message exchange will
779 * automatically be enlisted in the current transaction, if exists.
780 *
781 * @throws MessagingException
782 */
783 protected void autoEnlistInTx(MessageExchangeImpl me) throws MessagingException {
784 if (transactionManager != null && container.isAutoEnlistInTransaction() && !container.isUseNewTransactionModel()) {
785 try {
786 Transaction tx = transactionManager.getTransaction();
787 if (tx != null) {
788 Object oldTx = me.getTransactionContext();
789 if (oldTx == null) {
790 me.setTransactionContext(tx);
791 } else if (oldTx != tx) {
792 throw new IllegalStateException(
793 "the transaction context set in the messageExchange is not bound to the current thread");
794 }
795 }
796 } catch (Exception e) {
797 throw new MessagingException(e);
798 }
799 }
800 }
801
802 /**
803 * @return pretty print
804 */
805 public String toString() {
806 return "DeliveryChannel{" + component.getName() + "}";
807 }
808
809 /**
810 * Cancel all pending exchanges currently being handled by the DeliveryChannel
811 */
812 public void cancelPendingExchanges() {
813 for (String id : exchangesById.keySet()) {
814 MessageExchange exchange = exchangesById.get(id);
815 synchronized (exchange) {
816 exchange.notifyAll();
817 }
818 }
819 }
820
821 }