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 */
017package org.apache.camel.impl;
018
019import java.util.Map;
020
021import org.apache.camel.AsyncCallback;
022import org.apache.camel.AsyncProcessor;
023import org.apache.camel.CamelContext;
024import org.apache.camel.Consumer;
025import org.apache.camel.Endpoint;
026import org.apache.camel.EndpointConfiguration;
027import org.apache.camel.Exchange;
028import org.apache.camel.ExchangePattern;
029import org.apache.camel.PollingConsumer;
030import org.apache.camel.Processor;
031import org.apache.camel.Producer;
032import org.apache.camel.ShutdownableService;
033import org.apache.camel.util.ServiceHelper;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037import static org.apache.camel.processor.PipelineHelper.continueProcessing;
038
039/**
040 * This is an endpoint when sending to it, is intercepted and is routed in a detour
041 *
042 * @version 
043 */
044public class InterceptSendToEndpoint implements Endpoint, ShutdownableService {
045
046    private static final Logger LOG = LoggerFactory.getLogger(InterceptSendToEndpoint.class);
047
048    private final Endpoint delegate;
049    private Producer producer;
050    private Processor detour;
051    private boolean skip;
052
053    /**
054     * Intercepts sending to the given endpoint
055     *
056     * @param destination  the original endpoint
057     * @param skip <tt>true</tt> to skip sending after the detour to the original endpoint
058     */
059    public InterceptSendToEndpoint(final Endpoint destination, boolean skip) {
060        this.delegate = destination;
061        this.skip = skip;
062    }
063
064    public void setDetour(Processor detour) {
065        this.detour = detour;
066    }
067
068    public Endpoint getDelegate() {
069        return delegate;
070    }
071
072    public String getEndpointUri() {
073        return delegate.getEndpointUri();
074    }
075
076    public EndpointConfiguration getEndpointConfiguration() {
077        return delegate.getEndpointConfiguration();
078    }
079
080    public String getEndpointKey() {
081        return delegate.getEndpointKey();
082    }
083
084    public Exchange createExchange() {
085        return delegate.createExchange();
086    }
087
088    public Exchange createExchange(ExchangePattern pattern) {
089        return delegate.createExchange(pattern);
090    }
091
092    @Deprecated
093    public Exchange createExchange(Exchange exchange) {
094        return delegate.createExchange(exchange);
095    }
096
097    public CamelContext getCamelContext() {
098        return delegate.getCamelContext();
099    }
100
101    public Producer createProducer() throws Exception {
102        producer = delegate.createProducer();
103        return new DefaultAsyncProducer(delegate) {
104
105            public Endpoint getEndpoint() {
106                return producer.getEndpoint();
107            }
108
109            public Exchange createExchange() {
110                return producer.createExchange();
111            }
112
113            public Exchange createExchange(ExchangePattern pattern) {
114                return producer.createExchange(pattern);
115            }
116
117            @Deprecated
118            public Exchange createExchange(Exchange exchange) {
119                return producer.createExchange(exchange);
120            }
121
122            @Override
123            public boolean process(Exchange exchange, AsyncCallback callback) {
124                // process the detour so we do the detour routing
125                if (LOG.isDebugEnabled()) {
126                    LOG.debug("Sending to endpoint: {} is intercepted and detoured to: {} for exchange: {}", new Object[]{getEndpoint(), detour, exchange});
127                }
128                // add header with the real endpoint uri
129                exchange.getIn().setHeader(Exchange.INTERCEPTED_ENDPOINT, delegate.getEndpointUri());
130
131                // detour the exchange using synchronous processing
132                try {
133                    detour.process(exchange);
134                } catch (Exception e) {
135                    exchange.setException(e);
136                }
137
138                // Decide whether to continue or not; similar logic to the Pipeline
139                // check for error if so we should break out
140                if (!continueProcessing(exchange, "skip sending to original intended destination: " + getEndpoint(), LOG)) {
141                    callback.done(true);
142                    return true;
143                }
144
145                // determine if we should skip or not
146                boolean shouldSkip = skip;
147
148                // if then interceptor had a when predicate, then we should only skip if it matched
149                Boolean whenMatches = (Boolean) exchange.removeProperty(Exchange.INTERCEPT_SEND_TO_ENDPOINT_WHEN_MATCHED);
150                if (whenMatches != null) {
151                    shouldSkip = skip && whenMatches;
152                }
153
154                if (!shouldSkip) {
155                    if (exchange.hasOut()) {
156                        // replace OUT with IN as detour changed something
157                        exchange.setIn(exchange.getOut());
158                        exchange.setOut(null);
159                    }
160
161                    // route to original destination leveraging the asynchronous routing engine if possible
162                    if (producer instanceof AsyncProcessor) {
163                        AsyncProcessor async = (AsyncProcessor) producer;
164                        return async.process(exchange, callback);
165                    } else {
166                        try {
167                            producer.process(exchange);
168                        } catch (Exception e) {
169                            exchange.setException(e);
170                        }
171                        callback.done(true);
172                        return true;
173                    }
174                } else {
175                    if (LOG.isDebugEnabled()) {
176                        LOG.debug("Stop() means skip sending exchange to original intended destination: {} for exchange: {}", getEndpoint(), exchange);
177                    }
178                    callback.done(true);
179                    return true;
180                }
181            }
182
183            public boolean isSingleton() {
184                return producer.isSingleton();
185            }
186
187            public void start() throws Exception {
188                ServiceHelper.startService(detour);
189                // here we also need to start the producer
190                ServiceHelper.startService(producer);
191            }
192
193            public void stop() throws Exception {
194                // do not stop detour as it should only be stopped when the interceptor stops
195                // we should stop the producer here
196                ServiceHelper.stopService(producer);
197            }
198        };
199    }
200
201    public Consumer createConsumer(Processor processor) throws Exception {
202        return delegate.createConsumer(processor);
203    }
204
205    public PollingConsumer createPollingConsumer() throws Exception {
206        return delegate.createPollingConsumer();
207    }
208
209    public void configureProperties(Map<String, Object> options) {
210        delegate.configureProperties(options);
211    }
212
213    public void setCamelContext(CamelContext context) {
214        delegate.setCamelContext(context);
215    }
216
217    public boolean isLenientProperties() {
218        return delegate.isLenientProperties();
219    }
220
221    public boolean isSingleton() {
222        return delegate.isSingleton();
223    }
224
225    public void start() throws Exception {
226        ServiceHelper.startServices(detour, delegate);
227    }
228
229    public void stop() throws Exception {
230        ServiceHelper.stopServices(delegate, detour);
231    }
232
233    @Override
234    public void shutdown() throws Exception {
235        ServiceHelper.stopAndShutdownServices(delegate, detour);
236    }
237
238    @Override
239    public String toString() {
240        return delegate.toString();
241    }
242}