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