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.camel.processor;
018    
019    import org.apache.camel.AsyncCallback;
020    import org.apache.camel.AsyncProcessor;
021    import org.apache.camel.AsyncProducerCallback;
022    import org.apache.camel.CamelContext;
023    import org.apache.camel.Endpoint;
024    import org.apache.camel.Exchange;
025    import org.apache.camel.ExchangePattern;
026    import org.apache.camel.Producer;
027    import org.apache.camel.ProducerCallback;
028    import org.apache.camel.Traceable;
029    import org.apache.camel.impl.InterceptSendToEndpoint;
030    import org.apache.camel.impl.ProducerCache;
031    import org.apache.camel.support.ServiceSupport;
032    import org.apache.camel.util.AsyncProcessorHelper;
033    import org.apache.camel.util.ObjectHelper;
034    import org.apache.camel.util.ServiceHelper;
035    import org.apache.camel.util.URISupport;
036    import org.slf4j.Logger;
037    import org.slf4j.LoggerFactory;
038    
039    /**
040     * Processor for forwarding exchanges to an endpoint destination.
041     *
042     * @version 
043     */
044    public class SendProcessor extends ServiceSupport implements AsyncProcessor, Traceable {
045        protected final transient Logger log = LoggerFactory.getLogger(getClass());
046        protected final CamelContext camelContext;
047        protected ProducerCache producerCache;
048        protected Endpoint destination;
049        protected ExchangePattern pattern;
050    
051        public SendProcessor(Endpoint destination) {
052            ObjectHelper.notNull(destination, "destination");
053            this.destination = destination;
054            this.camelContext = destination.getCamelContext();
055            ObjectHelper.notNull(this.camelContext, "camelContext");
056        }
057    
058        public SendProcessor(Endpoint destination, ExchangePattern pattern) {
059            this(destination);
060            this.pattern = pattern;
061        }
062    
063        @Override
064        public String toString() {
065            return "sendTo(" + destination + (pattern != null ? " " + pattern : "") + ")";
066        }
067    
068        public void setDestination(Endpoint destination) {
069            this.destination = destination;
070            // destination changed so purge the cache
071            if (producerCache != null) {
072                producerCache.purge();
073            }
074        }
075    
076        public String getTraceLabel() {
077            return URISupport.sanitizeUri(destination.getEndpointUri());
078        }
079    
080        public void process(final Exchange exchange) throws Exception {
081            if (!isStarted()) {
082                throw new IllegalStateException("SendProcessor has not been started: " + this);
083            }
084    
085            // we should preserve existing MEP so remember old MEP
086            // if you want to permanently to change the MEP then use .setExchangePattern in the DSL
087            final ExchangePattern existingPattern = exchange.getPattern();
088    
089            // send the exchange to the destination using a producer
090            producerCache.doInProducer(destination, exchange, pattern, new ProducerCallback<Exchange>() {
091                public Exchange doInProducer(Producer producer, Exchange exchange, ExchangePattern pattern) throws Exception {
092                    exchange = configureExchange(exchange, pattern);
093                    log.debug(">>>> {} {}", destination, exchange);
094                    try {
095                        producer.process(exchange);
096                    } finally {
097                        // restore previous MEP
098                        exchange.setPattern(existingPattern);
099                    }
100                    return exchange;
101                }
102            });
103        }
104    
105        public boolean process(Exchange exchange, final AsyncCallback callback) {
106            if (!isStarted()) {
107                throw new IllegalStateException("SendProcessor has not been started: " + this);
108            }
109    
110            // we should preserve existing MEP so remember old MEP
111            // if you want to permanently to change the MEP then use .setExchangePattern in the DSL
112            final ExchangePattern existingPattern = exchange.getPattern();
113    
114            // send the exchange to the destination using a producer
115            return producerCache.doInAsyncProducer(destination, exchange, pattern, callback, new AsyncProducerCallback() {
116                public boolean doInAsyncProducer(Producer producer, AsyncProcessor asyncProducer, final Exchange exchange,
117                                                 ExchangePattern pattern, final AsyncCallback callback) {
118                    final Exchange target = configureExchange(exchange, pattern);
119                    log.debug(">>>> {} {}", destination, exchange);
120                    return AsyncProcessorHelper.process(asyncProducer, target, new AsyncCallback() {
121                        public void done(boolean doneSync) {
122                            // restore previous MEP
123                            target.setPattern(existingPattern);
124                            // signal we are done
125                            callback.done(doneSync);
126                        }
127                    });
128                }
129            });
130        }
131    
132        public Endpoint getDestination() {
133            return destination;
134        }
135    
136        public ExchangePattern getPattern() {
137            return pattern;
138        }
139    
140        protected Exchange configureExchange(Exchange exchange, ExchangePattern pattern) {
141            if (pattern != null) {
142                exchange.setPattern(pattern);
143            }
144            // set property which endpoint we send to
145            exchange.setProperty(Exchange.TO_ENDPOINT, destination.getEndpointUri());
146            return exchange;
147        }
148    
149        protected void doStart() throws Exception {
150            if (producerCache == null) {
151                // use a single producer cache as we need to only hold reference for one destination
152                producerCache = new ProducerCache(this, camelContext, 1);
153                // do not add as service as we do not want to manage the producer cache
154            }
155            ServiceHelper.startService(producerCache);
156    
157            // the destination could since have been intercepted by a interceptSendToEndpoint so we got to
158            // lookup this before we can use the destination
159            Endpoint lookup = camelContext.hasEndpoint(destination.getEndpointKey());
160            if (lookup instanceof InterceptSendToEndpoint) {
161                if (log.isDebugEnabled()) {
162                    log.debug("Intercepted sending to {} -> {}",
163                            URISupport.sanitizeUri(destination.getEndpointUri()), URISupport.sanitizeUri(lookup.getEndpointUri()));
164                }
165                destination = lookup;
166            }
167            // warm up the producer by starting it so we can fail fast if there was a problem
168            // however must start endpoint first
169            ServiceHelper.startService(destination);
170            producerCache.startProducer(destination);
171        }
172    
173        protected void doStop() throws Exception {
174            ServiceHelper.stopService(producerCache);
175        }
176    
177        protected void doShutdown() throws Exception {
178            ServiceHelper.stopAndShutdownService(producerCache);
179        }
180    }