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 java.util.concurrent.Callable;
020    import java.util.concurrent.ExecutorService;
021    
022    import org.apache.camel.Exchange;
023    import org.apache.camel.ExchangePattern;
024    import org.apache.camel.Predicate;
025    import org.apache.camel.Processor;
026    import org.apache.camel.impl.ServiceSupport;
027    import org.apache.camel.impl.SynchronizationAdapter;
028    import org.apache.camel.util.ExchangeHelper;
029    import org.apache.camel.util.ServiceHelper;
030    import org.apache.camel.util.concurrent.ExecutorServiceHelper;
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogFactory;
033    
034    /**
035     * @version $Revision: 836215 $
036     */
037    public class OnCompletionProcessor extends ServiceSupport implements Processor, Traceable {
038    
039        private static final int DEFAULT_THREADPOOL_SIZE = 10;
040        private static final transient Log LOG = LogFactory.getLog(OnCompletionProcessor.class);
041        private ExecutorService executorService;
042        private Processor processor;
043        private boolean onCompleteOnly;
044        private boolean onFailureOnly;
045        private Predicate onWhen;
046    
047        public OnCompletionProcessor(Processor processor, boolean onCompleteOnly, boolean onFailureOnly, Predicate onWhen) {
048            // wrap processor in UnitOfWork so what we send out runs in a UoW
049            this.processor = new UnitOfWorkProcessor(processor);
050            this.onCompleteOnly = onCompleteOnly;
051            this.onFailureOnly = onFailureOnly;
052            this.onWhen = onWhen;
053        }
054    
055        protected void doStart() throws Exception {
056            ServiceHelper.startService(processor);
057        }
058    
059        @Override
060        protected void doStop() throws Exception {
061            if (executorService != null) {
062                executorService.shutdown();
063                // must null it so we can restart
064                executorService = null;
065            }
066            ServiceHelper.stopService(processor);
067        }
068    
069        public void process(Exchange exchange) throws Exception {
070            if (processor == null) {
071                return;
072            }
073    
074            // register callback
075            exchange.getUnitOfWork().addSynchronization(new SynchronizationAdapter() {
076                @Override
077                public void onComplete(Exchange exchange) {
078                    if (onFailureOnly) {
079                        return;
080                    }
081    
082                    if (onWhen != null && !onWhen.matches(exchange)) {
083                        // predicate did not match so do not route the onComplete
084                        return;
085                    }
086    
087                    // must use a copy as we dont want it to cause side effects of the original exchange
088                    final Exchange copy = prepareExchange(exchange);
089    
090                    getExecutorService().submit(new Callable<Exchange>() {
091                        public Exchange call() throws Exception {
092                            if (LOG.isDebugEnabled()) {
093                                LOG.debug("Processing onComplete: " + copy);
094                            }
095                            doProcess(processor, copy);
096                            return copy;
097                        }
098                    });
099                }
100    
101                public void onFailure(Exchange exchange) {
102                    if (onCompleteOnly) {
103                        return;
104                    }
105    
106                    if (onWhen != null && !onWhen.matches(exchange)) {
107                        // predicate did not match so do not route the onComplete
108                        return;
109                    }
110    
111                    // must use a copy as we dont want it to cause side effects of the original exchange
112                    final Exchange copy = prepareExchange(exchange);
113                    // must remove exception otherwise onFailure routing will fail as well
114                    // the caused exception is stored as a property (Exchange.EXCEPTION_CAUGHT) on the exchange
115                    copy.setException(null);
116    
117                    getExecutorService().submit(new Callable<Exchange>() {
118                        public Exchange call() throws Exception {
119                            if (LOG.isDebugEnabled()) {
120                                LOG.debug("Processing onFailure: " + copy);
121                            }
122                            doProcess(processor, copy);
123                            return copy;
124                        }
125                    });
126                }
127    
128                @Override
129                public String toString() {
130                    if (!onCompleteOnly && !onFailureOnly) {
131                        return "onCompleteOrFailure";
132                    } else if (onCompleteOnly) {
133                        return "onCompleteOnly";
134                    } else {
135                        return "onFailureOnly";
136                    }
137                }
138            });
139        }
140    
141        /**
142         * Processes the exchange by the processors
143         *
144         * @param processor the processor
145         * @param exchange the exchange
146         */
147        protected static void doProcess(Processor processor, Exchange exchange) {
148            try {
149                processor.process(exchange);
150            } catch (Exception e) {
151                exchange.setException(e);
152            }
153        }
154    
155    
156        /**
157         * Prepares the {@link Exchange} to send as onCompletion.
158         *
159         * @param exchange the current exchange
160         * @return the exchange to be routed in onComplete
161         */
162        protected Exchange prepareExchange(Exchange exchange) {
163            // must use a copy as we dont want it to cause side effects of the original exchange
164            final Exchange copy = ExchangeHelper.createCorrelatedCopy(exchange, false);
165            // set MEP to InOnly as this wire tap is a fire and forget
166            copy.setPattern(ExchangePattern.InOnly);
167            // add a header flag to indicate its a on completion exchange
168            copy.setProperty(Exchange.ON_COMPLETION, Boolean.TRUE);
169            return copy;
170        }
171    
172        public ExecutorService getExecutorService() {
173            if (executorService == null) {
174                executorService = createExecutorService();
175            }
176            return executorService;
177        }
178    
179        private ExecutorService createExecutorService() {
180            return ExecutorServiceHelper.newScheduledThreadPool(DEFAULT_THREADPOOL_SIZE, this.toString(), true);
181        }
182    
183        public void setExecutorService(ExecutorService executorService) {
184            this.executorService = executorService;
185        }
186    
187        @Override
188        public String toString() {
189            return "OnCompletionProcessor[" + processor + "]";
190        }
191    
192        public String getTraceLabel() {
193            return "onCompletion";
194        }
195    }