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 }