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.model; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Map; 025import java.util.concurrent.ExecutorService; 026import javax.xml.bind.annotation.XmlAccessType; 027import javax.xml.bind.annotation.XmlAccessorType; 028import javax.xml.bind.annotation.XmlAttribute; 029import javax.xml.bind.annotation.XmlElement; 030import javax.xml.bind.annotation.XmlElementRef; 031import javax.xml.bind.annotation.XmlRootElement; 032import javax.xml.bind.annotation.XmlTransient; 033 034import org.apache.camel.Predicate; 035import org.apache.camel.Processor; 036import org.apache.camel.processor.CamelInternalProcessor; 037import org.apache.camel.processor.OnCompletionProcessor; 038import org.apache.camel.spi.Metadata; 039import org.apache.camel.spi.RouteContext; 040 041/** 042 * Route to be executed when normal route processing completes 043 * 044 * @version 045 */ 046@Metadata(label = "configuration") 047@XmlRootElement(name = "onCompletion") 048@XmlAccessorType(XmlAccessType.FIELD) 049public class OnCompletionDefinition extends ProcessorDefinition<OnCompletionDefinition> implements ExecutorServiceAwareDefinition<OnCompletionDefinition> { 050 @XmlAttribute @Metadata(defaultValue = "AfterConsumer") 051 private OnCompletionMode mode; 052 @XmlAttribute 053 private Boolean onCompleteOnly; 054 @XmlAttribute 055 private Boolean onFailureOnly; 056 @XmlElement(name = "onWhen") 057 private WhenDefinition onWhen; 058 @XmlAttribute 059 private Boolean parallelProcessing; 060 @XmlAttribute 061 private String executorServiceRef; 062 @XmlAttribute(name = "useOriginalMessage") 063 private Boolean useOriginalMessagePolicy; 064 @XmlElementRef 065 private List<ProcessorDefinition<?>> outputs = new ArrayList<ProcessorDefinition<?>>(); 066 @XmlTransient 067 private ExecutorService executorService; 068 @XmlTransient 069 private Boolean routeScoped; 070 // TODO: in Camel 3.0 the OnCompletionDefinition should not contain state and OnCompletion processors 071 @XmlTransient 072 private final Map<String, Processor> onCompletions = new HashMap<String, Processor>(); 073 074 public OnCompletionDefinition() { 075 } 076 077 public boolean isRouteScoped() { 078 // is context scoped by default 079 return routeScoped != null ? routeScoped : false; 080 } 081 082 public Processor getOnCompletion(String routeId) { 083 return onCompletions.get(routeId); 084 } 085 086 public Collection<Processor> getOnCompletions() { 087 return onCompletions.values(); 088 } 089 090 @Override 091 public String toString() { 092 return "onCompletion[" + getOutputs() + "]"; 093 } 094 095 @Override 096 public String getLabel() { 097 return "onCompletion"; 098 } 099 100 @Override 101 public boolean isAbstract() { 102 return true; 103 } 104 105 @Override 106 public boolean isTopLevelOnly() { 107 return true; 108 } 109 110 @Override 111 public Processor createProcessor(RouteContext routeContext) throws Exception { 112 // assign whether this was a route scoped onCompletion or not 113 // we need to know this later when setting the parent, as only route scoped should have parent 114 // Note: this logic can possible be removed when the Camel routing engine decides at runtime 115 // to apply onCompletion in a more dynamic fashion than current code base 116 // and therefore is in a better position to decide among context/route scoped OnCompletion at runtime 117 if (routeScoped == null) { 118 routeScoped = super.getParent() != null; 119 } 120 121 boolean isOnCompleteOnly = getOnCompleteOnly() != null && getOnCompleteOnly(); 122 boolean isOnFailureOnly = getOnFailureOnly() != null && getOnFailureOnly(); 123 boolean isParallelProcessing = getParallelProcessing() != null && getParallelProcessing(); 124 boolean original = getUseOriginalMessagePolicy() != null && getUseOriginalMessagePolicy(); 125 126 if (isOnCompleteOnly && isOnFailureOnly) { 127 throw new IllegalArgumentException("Both onCompleteOnly and onFailureOnly cannot be true. Only one of them can be true. On node: " + this); 128 } 129 130 String routeId = routeContext.getRoute().idOrCreate(routeContext.getCamelContext().getNodeIdFactory()); 131 132 Processor childProcessor = this.createChildProcessor(routeContext, true); 133 134 // wrap the on completion route in a unit of work processor 135 CamelInternalProcessor internal = new CamelInternalProcessor(childProcessor); 136 internal.addAdvice(new CamelInternalProcessor.UnitOfWorkProcessorAdvice(routeContext)); 137 138 onCompletions.put(routeId, internal); 139 140 Predicate when = null; 141 if (onWhen != null) { 142 when = onWhen.getExpression().createPredicate(routeContext); 143 } 144 145 boolean shutdownThreadPool = ProcessorDefinitionHelper.willCreateNewThreadPool(routeContext, this, isParallelProcessing); 146 ExecutorService threadPool = ProcessorDefinitionHelper.getConfiguredExecutorService(routeContext, "OnCompletion", this, isParallelProcessing); 147 148 // should be after consumer by default 149 boolean afterConsumer = mode == null || mode == OnCompletionMode.AfterConsumer; 150 151 OnCompletionProcessor answer = new OnCompletionProcessor(routeContext.getCamelContext(), internal, 152 threadPool, shutdownThreadPool, isOnCompleteOnly, isOnFailureOnly, when, original, afterConsumer); 153 return answer; 154 } 155 156 /** 157 * Removes all existing {@link org.apache.camel.model.OnCompletionDefinition} from the definition. 158 * <p/> 159 * This is used to let route scoped <tt>onCompletion</tt> overrule any global <tt>onCompletion</tt>. 160 * Hence we remove all existing as they are global. 161 * 162 * @param definition the parent definition that is the route 163 */ 164 public void removeAllOnCompletionDefinition(ProcessorDefinition<?> definition) { 165 for (Iterator<ProcessorDefinition<?>> it = definition.getOutputs().iterator(); it.hasNext();) { 166 ProcessorDefinition<?> out = it.next(); 167 if (out instanceof OnCompletionDefinition) { 168 it.remove(); 169 } 170 } 171 } 172 173 @Override 174 public ProcessorDefinition<?> end() { 175 // pop parent block, as we added our self as block to parent when synchronized was defined in the route 176 getParent().popBlock(); 177 return super.end(); 178 } 179 180 /** 181 * Sets the mode to be after route is done (default due backwards compatible). 182 * <p/> 183 * This executes the on completion work <i>after</i> the route consumer have written response 184 * back to the callee (if its InOut mode). 185 * 186 * @return the builder 187 */ 188 public OnCompletionDefinition modeAfterConsumer() { 189 setMode(OnCompletionMode.AfterConsumer); 190 return this; 191 } 192 193 /** 194 * Sets the mode to be before consumer is done. 195 * <p/> 196 * This allows the on completion work to execute <i>before</i> the route consumer, writes any response 197 * back to the callee (if its InOut mode). 198 * 199 * @return the builder 200 */ 201 public OnCompletionDefinition modeBeforeConsumer() { 202 setMode(OnCompletionMode.BeforeConsumer); 203 return this; 204 } 205 206 /** 207 * Will only synchronize when the {@link org.apache.camel.Exchange} completed successfully (no errors). 208 * 209 * @return the builder 210 */ 211 public OnCompletionDefinition onCompleteOnly() { 212 boolean isOnFailureOnly = getOnFailureOnly() != null && getOnFailureOnly(); 213 if (isOnFailureOnly) { 214 throw new IllegalArgumentException("Both onCompleteOnly and onFailureOnly cannot be true. Only one of them can be true. On node: " + this); 215 } 216 // must define return type as OutputDefinition and not this type to avoid end user being able 217 // to invoke onFailureOnly/onCompleteOnly more than once 218 setOnCompleteOnly(Boolean.TRUE); 219 setOnFailureOnly(Boolean.FALSE); 220 return this; 221 } 222 223 /** 224 * Will only synchronize when the {@link org.apache.camel.Exchange} ended with failure (exception or FAULT message). 225 * 226 * @return the builder 227 */ 228 public OnCompletionDefinition onFailureOnly() { 229 boolean isOnCompleteOnly = getOnCompleteOnly() != null && getOnCompleteOnly(); 230 if (isOnCompleteOnly) { 231 throw new IllegalArgumentException("Both onCompleteOnly and onFailureOnly cannot be true. Only one of them can be true. On node: " + this); 232 } 233 // must define return type as OutputDefinition and not this type to avoid end user being able 234 // to invoke onFailureOnly/onCompleteOnly more than once 235 setOnCompleteOnly(Boolean.FALSE); 236 setOnFailureOnly(Boolean.TRUE); 237 return this; 238 } 239 240 /** 241 * Sets an additional predicate that should be true before the onCompletion is triggered. 242 * <p/> 243 * To be used for fine grained controlling whether a completion callback should be invoked or not 244 * 245 * @param predicate predicate that determines true or false 246 * @return the builder 247 */ 248 public OnCompletionDefinition onWhen(Predicate predicate) { 249 setOnWhen(new WhenDefinition(predicate)); 250 return this; 251 } 252 253 /** 254 * Will use the original input body when an {@link org.apache.camel.Exchange} for this on completion. 255 * <p/> 256 * By default this feature is off. 257 * 258 * @return the builder 259 */ 260 public OnCompletionDefinition useOriginalBody() { 261 setUseOriginalMessagePolicy(Boolean.TRUE); 262 return this; 263 } 264 265 /** 266 * To use a custom Thread Pool to be used for parallel processing. 267 * Notice if you set this option, then parallel processing is automatic implied, and you do not have to enable that option as well. 268 */ 269 public OnCompletionDefinition executorService(ExecutorService executorService) { 270 setExecutorService(executorService); 271 return this; 272 } 273 274 /** 275 * Refers to a custom Thread Pool to be used for parallel processing. 276 * Notice if you set this option, then parallel processing is automatic implied, and you do not have to enable that option as well. 277 */ 278 public OnCompletionDefinition executorServiceRef(String executorServiceRef) { 279 setExecutorServiceRef(executorServiceRef); 280 return this; 281 } 282 283 /** 284 * If enabled then the on completion process will run asynchronously by a separate thread from a thread pool. 285 * By default this is false, meaning the on completion process will run synchronously using the same caller thread as from the route. 286 * 287 * @return the builder 288 */ 289 public OnCompletionDefinition parallelProcessing() { 290 setParallelProcessing(true); 291 return this; 292 } 293 294 /** 295 * If enabled then the on completion process will run asynchronously by a separate thread from a thread pool. 296 * By default this is false, meaning the on completion process will run synchronously using the same caller thread as from the route. 297 * 298 * @return the builder 299 */ 300 public OnCompletionDefinition parallelProcessing(boolean parallelProcessing) { 301 setParallelProcessing(parallelProcessing); 302 return this; 303 } 304 305 public List<ProcessorDefinition<?>> getOutputs() { 306 return outputs; 307 } 308 309 public void setOutputs(List<ProcessorDefinition<?>> outputs) { 310 this.outputs = outputs; 311 } 312 313 public boolean isOutputSupported() { 314 return true; 315 } 316 317 public OnCompletionMode getMode() { 318 return mode; 319 } 320 321 /** 322 * Sets the on completion mode. 323 * <p/> 324 * The default value is AfterConsumer 325 */ 326 public void setMode(OnCompletionMode mode) { 327 this.mode = mode; 328 } 329 330 public Boolean getOnCompleteOnly() { 331 return onCompleteOnly; 332 } 333 334 public void setOnCompleteOnly(Boolean onCompleteOnly) { 335 this.onCompleteOnly = onCompleteOnly; 336 } 337 338 public Boolean getOnFailureOnly() { 339 return onFailureOnly; 340 } 341 342 public void setOnFailureOnly(Boolean onFailureOnly) { 343 this.onFailureOnly = onFailureOnly; 344 } 345 346 public WhenDefinition getOnWhen() { 347 return onWhen; 348 } 349 350 public void setOnWhen(WhenDefinition onWhen) { 351 this.onWhen = onWhen; 352 } 353 354 public ExecutorService getExecutorService() { 355 return executorService; 356 } 357 358 public void setExecutorService(ExecutorService executorService) { 359 this.executorService = executorService; 360 } 361 362 public String getExecutorServiceRef() { 363 return executorServiceRef; 364 } 365 366 public void setExecutorServiceRef(String executorServiceRef) { 367 this.executorServiceRef = executorServiceRef; 368 } 369 370 public Boolean getUseOriginalMessagePolicy() { 371 return useOriginalMessagePolicy; 372 } 373 374 /** 375 * Will use the original input body when an {@link org.apache.camel.Exchange} for this on completion. 376 * <p/> 377 * By default this feature is off. 378 */ 379 public void setUseOriginalMessagePolicy(Boolean useOriginalMessagePolicy) { 380 this.useOriginalMessagePolicy = useOriginalMessagePolicy; 381 } 382 383 public Boolean getParallelProcessing() { 384 return parallelProcessing; 385 } 386 387 public void setParallelProcessing(Boolean parallelProcessing) { 388 this.parallelProcessing = parallelProcessing; 389 } 390 391}