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.processor;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.List;
022import java.util.concurrent.CountDownLatch;
023import java.util.concurrent.RejectedExecutionException;
024
025import org.apache.camel.AsyncCallback;
026import org.apache.camel.AsyncProcessor;
027import org.apache.camel.Exchange;
028import org.apache.camel.Ordered;
029import org.apache.camel.Processor;
030import org.apache.camel.Service;
031import org.apache.camel.spi.AsyncProcessorAwaitManager;
032import org.apache.camel.spi.RoutePolicy;
033import org.apache.camel.spi.Transformer;
034import org.apache.camel.spi.UnitOfWork;
035import org.apache.camel.util.OrderedComparator;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039/**
040 * A Shared (thread safe) internal {@link Processor} that Camel routing engine used during routing for cross cutting functionality such as:
041 * <ul>
042 *     <li>Execute {@link UnitOfWork}</li>
043 *     <li>Keeping track which route currently is being routed</li>
044 *     <li>Execute {@link RoutePolicy}</li>
045 *     <li>Gather JMX performance statics</li>
046 *     <li>Tracing</li>
047 *     <li>Debugging</li>
048 *     <li>Message History</li>
049 *     <li>Stream Caching</li>
050 *     <li>{@link Transformer}</li>
051 * </ul>
052 * ... and more.
053 * <p/>
054 * This implementation executes this cross cutting functionality as a {@link CamelInternalProcessorAdvice} advice (before and after advice)
055 * by executing the {@link CamelInternalProcessorAdvice#before(Exchange)} and
056 * {@link CamelInternalProcessorAdvice#after(Exchange, Object)} callbacks in correct order during routing.
057 * This reduces number of stack frames needed during routing, and reduce the number of lines in stacktraces, as well
058 * makes debugging the routing engine easier for end users.
059 * <p/>
060 * <b>Debugging tips:</b> Camel end users whom want to debug their Camel applications with the Camel source code, then make sure to
061 * read the source code of this class about the debugging tips, which you can find in the
062 * {@link #process(Exchange, AsyncCallback, AsyncProcessor, Processor)} method.
063 * <p/>
064 * The added advices can implement {@link Ordered} to control in which order the advices are executed.
065 */
066public class SharedCamelInternalProcessor {
067
068    private static final Logger LOG = LoggerFactory.getLogger(SharedCamelInternalProcessor.class);
069    private final List<CamelInternalProcessorAdvice> advices = new ArrayList<>();
070
071    public SharedCamelInternalProcessor(CamelInternalProcessorAdvice... advices) {
072        if (advices != null) {
073            this.advices.addAll(Arrays.asList(advices));
074            // ensure advices are sorted so they are in the order we want
075            this.advices.sort(OrderedComparator.get());
076        }
077    }
078
079    /**
080     * Synchronous API
081     */
082    public void process(Exchange exchange, AsyncProcessor processor, Processor resultProcessor) {
083        final AsyncProcessorAwaitManager awaitManager = exchange.getContext().getAsyncProcessorAwaitManager();
084        final CountDownLatch latch = new CountDownLatch(1);
085
086        boolean sync = process(exchange, new AsyncCallback() {
087            public void done(boolean doneSync) {
088                if (!doneSync) {
089                    awaitManager.countDown(exchange, latch);
090                }
091            }
092
093            @Override
094            public String toString() {
095                return "Done " + processor;
096            }
097        }, processor, resultProcessor);
098
099        if (!sync) {
100            awaitManager.await(exchange, latch);
101        }
102    }
103
104    /**
105     * Asynchronous API
106     */
107    public boolean process(Exchange exchange, AsyncCallback callback, AsyncProcessor processor, Processor resultProcessor) {
108        // ----------------------------------------------------------
109        // CAMEL END USER - READ ME FOR DEBUGGING TIPS
110        // ----------------------------------------------------------
111        // If you want to debug the Camel routing engine, then there is a lot of internal functionality
112        // the routing engine executes during routing messages. You can skip debugging this internal
113        // functionality and instead debug where the routing engine continues routing to the next node
114        // in the routes. The CamelInternalProcessor is a vital part of the routing engine, as its
115        // being used in between the nodes. As an end user you can just debug the code in this class
116        // in between the:
117        //   CAMEL END USER - DEBUG ME HERE +++ START +++
118        //   CAMEL END USER - DEBUG ME HERE +++ END +++
119        // you can see in the code below.
120        // ----------------------------------------------------------
121
122        if (processor == null || !continueProcessing(exchange, processor)) {
123            // no processor or we should not continue then we are done
124            callback.done(true);
125            return true;
126        }
127
128        // optimise to use object array for states
129        final Object[] states = new Object[advices.size()];
130        // optimise for loop using index access to avoid creating iterator object
131        for (int i = 0; i < advices.size(); i++) {
132            CamelInternalProcessorAdvice task = advices.get(i);
133            try {
134                Object state = task.before(exchange);
135                states[i] = state;
136            } catch (Throwable e) {
137                exchange.setException(e);
138                callback.done(true);
139                return true;
140            }
141        }
142
143        // create internal callback which will execute the advices in reverse order when done
144        callback = new InternalCallback(states, exchange, callback, resultProcessor);
145
146        // UNIT_OF_WORK_PROCESS_SYNC is @deprecated and we should remove it from Camel 3.0
147        Object synchronous = exchange.removeProperty(Exchange.UNIT_OF_WORK_PROCESS_SYNC);
148        if (exchange.isTransacted() || synchronous != null) {
149            // must be synchronized for transacted exchanges
150            if (LOG.isTraceEnabled()) {
151                if (exchange.isTransacted()) {
152                    LOG.trace("Transacted Exchange must be routed synchronously for exchangeId: {} -> {}", exchange.getExchangeId(), exchange);
153                } else {
154                    LOG.trace("Synchronous UnitOfWork Exchange must be routed synchronously for exchangeId: {} -> {}", exchange.getExchangeId(), exchange);
155                }
156            }
157            // ----------------------------------------------------------
158            // CAMEL END USER - DEBUG ME HERE +++ START +++
159            // ----------------------------------------------------------
160            try {
161                processor.process(exchange);
162            } catch (Throwable e) {
163                exchange.setException(e);
164            }
165            // ----------------------------------------------------------
166            // CAMEL END USER - DEBUG ME HERE +++ END +++
167            // ----------------------------------------------------------
168            callback.done(true);
169            return true;
170        } else {
171            final UnitOfWork uow = exchange.getUnitOfWork();
172
173            // allow unit of work to wrap callback in case it need to do some special work
174            // for example the MDCUnitOfWork
175            AsyncCallback async = callback;
176            if (uow != null) {
177                async = uow.beforeProcess(processor, exchange, callback);
178            }
179
180            // ----------------------------------------------------------
181            // CAMEL END USER - DEBUG ME HERE +++ START +++
182            // ----------------------------------------------------------
183            if (LOG.isTraceEnabled()) {
184                LOG.trace("Processing exchange for exchangeId: {} -> {}", exchange.getExchangeId(), exchange);
185            }
186            boolean sync = processor.process(exchange, async);
187            // ----------------------------------------------------------
188            // CAMEL END USER - DEBUG ME HERE +++ END +++
189            // ----------------------------------------------------------
190
191            // execute any after processor work (in current thread, not in the callback)
192            if (uow != null) {
193                uow.afterProcess(processor, exchange, callback, sync);
194            }
195
196            if (LOG.isTraceEnabled()) {
197                LOG.trace("Exchange processed and is continued routed {} for exchangeId: {} -> {}",
198                        new Object[]{sync ? "synchronously" : "asynchronously", exchange.getExchangeId(), exchange});
199            }
200            return sync;
201        }
202    }
203
204    /**
205     * Internal callback that executes the after advices.
206     */
207    private final class InternalCallback implements AsyncCallback {
208
209        private final Object[] states;
210        private final Exchange exchange;
211        private final AsyncCallback callback;
212        private final Processor resultProcessor;
213
214        private InternalCallback(Object[] states, Exchange exchange, AsyncCallback callback, Processor resultProcessor) {
215            this.states = states;
216            this.exchange = exchange;
217            this.callback = callback;
218            this.resultProcessor = resultProcessor;
219        }
220
221        @Override
222        @SuppressWarnings("unchecked")
223        public void done(boolean doneSync) {
224            // NOTE: if you are debugging Camel routes, then all the code in the for loop below is internal only
225            // so you can step straight to the finally block and invoke the callback
226
227            if (resultProcessor != null) {
228                try {
229                    resultProcessor.process(exchange);
230                } catch (Throwable e) {
231                    exchange.setException(e);
232                }
233            }
234
235            // we should call after in reverse order
236            try {
237                for (int i = advices.size() - 1; i >= 0; i--) {
238                    CamelInternalProcessorAdvice task = advices.get(i);
239                    Object state = states[i];
240                    try {
241                        task.after(exchange, state);
242                    } catch (Throwable e) {
243                        exchange.setException(e);
244                        // allow all advices to complete even if there was an exception
245                    }
246                }
247            } finally {
248                // ----------------------------------------------------------
249                // CAMEL END USER - DEBUG ME HERE +++ START +++
250                // ----------------------------------------------------------
251                // callback must be called
252                callback.done(doneSync);
253                // ----------------------------------------------------------
254                // CAMEL END USER - DEBUG ME HERE +++ END +++
255                // ----------------------------------------------------------
256            }
257        }
258    }
259
260    /**
261     * Strategy to determine if we should continue processing the {@link Exchange}.
262     */
263    protected boolean continueProcessing(Exchange exchange, AsyncProcessor processor) {
264        Object stop = exchange.getProperty(Exchange.ROUTE_STOP);
265        if (stop != null) {
266            boolean doStop = exchange.getContext().getTypeConverter().convertTo(Boolean.class, stop);
267            if (doStop) {
268                LOG.debug("Exchange is marked to stop routing: {}", exchange);
269                return false;
270            }
271        }
272
273        // determine if we can still run, or the camel context is forcing a shutdown
274        if (processor instanceof Service) {
275            boolean forceShutdown = exchange.getContext().getShutdownStrategy().forceShutdown((Service) processor);
276            if (forceShutdown) {
277                String msg = "Run not allowed as ShutdownStrategy is forcing shutting down, will reject executing exchange: " + exchange;
278                LOG.debug(msg);
279                if (exchange.getException() == null) {
280                    exchange.setException(new RejectedExecutionException(msg));
281                }
282                return false;
283            }
284        }
285
286        // yes we can continue
287        return true;
288    }
289
290}