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    }